Mocking $http
requests in AngularJS tests allows you to simulate HTTP interactions without making real API calls. This is crucial for testing your application’s logic, especially when dealing with external services or when the network is not available. AngularJS provides $httpBackend
(part of ngMock
) for mocking HTTP requests during unit testing.
Here’s a step-by-step guide to mock $http
requests in AngularJS tests, and how to write unit tests that verify the behavior of your application when interacting with APIs.
1. Setting Up the Testing Environment
To begin, ensure that you have the following dependencies:
- Jasmine: For writing unit tests.
- Karma: To run tests in the browser.
- ngMock: To mock AngularJS services such as
$httpBackend
.
When setting up your testing module, ensure you include ngMock
and any dependencies required by the service or controller you’re testing.
2. Example Service Code to Test
Consider a simple AngularJS service that performs HTTP GET and POST requests:
angular.module('myApp', [])
.service('MyService', function($http) {
var apiUrl = '/api/data';
// GET request to retrieve data
this.getData = function() {
return $http.get(apiUrl);
};
// POST request to save data
this.saveData = function(data) {
return $http.post(apiUrl, data);
};
});
In this example, the MyService
service has two methods:
getData()
to fetch data from/api/data
.saveData(data)
to send data to the API.
3. Writing the Test Suite
Here’s how you can mock $http
requests using $httpBackend
and write tests for this service.
Step 1: Set up the test module and inject dependencies
In the beforeEach
block, you should load the module that contains the service and inject $httpBackend
, the service, and any other necessary dependencies.
describe('MyService', function() {
var MyService, $httpBackend;
// Load the module containing MyService
beforeEach(module('myApp'));
// Inject necessary services
beforeEach(inject(function(_MyService_, _$httpBackend_) {
MyService = _MyService_;
$httpBackend = _$httpBackend_;
}));
// Clear outstanding expectations and requests after each test
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
});
Step 2: Mock the HTTP request
You need to mock the HTTP request using $httpBackend.whenXXX()
methods. Here, whenGET()
and whenPOST()
are used to mock the GET and POST requests.
Step 3: Write the test case
Test Case 1: Testing GET Request
We’ll write a test for the getData()
method of MyService
:
it('should retrieve data from the API', function() {
var mockData = [{ id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }];
// Mock the GET request
$httpBackend.whenGET('/api/data').respond(200, mockData);
// Call the service method
MyService.getData().then(function(response) {
expect(response.data).toEqual(mockData); // Verify the response data
});
// Flush the request to simulate the backend response
$httpBackend.flush();
});
Explanation:
- $httpBackend.whenGET(‘/api/data’): This mocks a GET request to
/api/data
and defines a mock response (mockData
). - $httpBackend.flush(): This forces the request to be processed and the response to be returned.
- expect(response.data): Verifies that the response data matches
mockData
.
Test Case 2: Testing POST Request
We’ll write a test for the saveData(data)
method of MyService
:
it('should save data to the API', function() {
var postData = { name: 'New Item' };
var mockResponse = { id: 3, name: 'New Item' };
// Mock the POST request
$httpBackend.whenPOST('/api/data').respond(201, mockResponse);
// Call the service method
MyService.saveData(postData).then(function(response) {
expect(response.data).toEqual(mockResponse); // Verify the response data
});
// Flush the request to simulate the backend response
$httpBackend.flush();
});
Explanation:
- $httpBackend.whenPOST(‘/api/data’): This mocks a POST request to
/api/data
withpostData
and returns amockResponse
. - expect(response.data): Verifies that the returned data matches
mockResponse
.
4. Error Handling in Mocked Requests
Testing how your service behaves when there’s an error from the backend is equally important. You can mock errors by setting the response code to 4xx or 5xx and checking if the service handles it correctly.
Test Case: Testing Error Handling
it('should handle error when API request fails', function() {
var mockError = { message: 'Internal Server Error' };
// Mock the GET request to simulate an error
$httpBackend.whenGET('/api/data').respond(500, mockError);
// Call the service method
MyService.getData().catch(function(error) {
expect(error.status).toBe(500); // Check the error status
expect(error.data.message).toBe('Internal Server Error'); // Verify the error message
});
// Flush the request to simulate the backend error
$httpBackend.flush();
});
In this test:
- We simulate a 500 error using
$httpBackend.whenGET()
with therespond()
method. - We check that the error status and message are handled correctly in the service’s
.catch()
block.
5. Verifying No Outstanding HTTP Requests
It’s essential to verify that there are no outstanding requests after each test to ensure that the test environment is clean and no HTTP requests are left hanging.
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
This ensures that all expected requests were made and no extra requests were pending.