I'm working on a project and we've begun to write Jasmine unit tests. This application, like any good JS app does a lot of asynchronous fetching of data. I see that angular provides $httpBackend to mock up an HTTP request. I've also read and heard that it's a bad idea to test AJAX requests in your controller, and thus the raison d'etre for $httpBackend. Why is it not a good idea to test AJAX calls? How do big JS applications get around this fact? When does the actual testing of hitting the actual servers happen?
Asserting that you shouldn't be testing the server from Jasmine is an overly simple generalization. Whether you want small tests, large tests, or some combination of both really depends on the application. One might say that it isn't a "unit" test anymore - that it's an "integration" test - but those terms have been overloaded to the point of nonsense because it's a rather artificial dichotomy. Write tests that are useful and maintainable. Choosing the size of the test is an important part of that. What size is appropriate requires your own judgement of the situation.
The idea of a unit test is to outline how a small part of an application should work. If it is passing that test, you know that it is functioning as specified.
If you want to test a service that sends requests to your API, you set up unit tests that check the location/content/existence of the outgoing requests and verifies that it responds correctly to a valid response. To make the test as specific as possible though, it should not test anything outside of that little bubble.
Let's say we want to test this PersonService
angular service:
app.service('PersonService', function ($q, $timeout, $http) {
this.addPerson = function(data) {
var deferred = $q.defer();
$http.post('api/People', data).then(function(response) {
deferred.resolve(response);
});
return deferred.promise;
};
});
Here is a simple Jasmine test:
var service, $httpBackend;
beforeEach(module('app'));
beforeEach(inject(function(_PersonService_, _$httpBackend_) {
service = _PersonService_;
$httpBackend = _$httpBackend_;
}));
describe('addPerson', function() {
var person;
beforeEach(function() {
// Make a person to send
person = new Person({ id: "0ff1165f-7f75-45ed-8faa-ee94d874a1cf" });
});
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should send a person object to the API', function() {
// setup
var promise, expected, actual;
expected = /* Whatever is sent back */
$httpBackend.whenPOST('api/People', person)
.respond(expected);
// act
promise = service.addPerson(person);
promise.then(function(response) {
actual = response.data;
});
$httpBackend.flush();
// assert
expect(actual).toEqual(expected);
});
});
So we have a test that checks this very basic functionality (regardless of what the API actually does). This allows us to test pieces of the code individually---ie if the API is broken, your PersonService
test won't break in addition to the related API test.
What you want to build to make sure everything works together properly is integration tests.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With