Reputation: 2961
I have been fighting with this for a little bit now and need some guidance. I would like to unit test this angular service... specifically, the failed part of the promise.
(function () {
angular.module('testable')
.factory('myService', ["$http", "$q", function ($http, $q) {
return {
createThing: function(thing) {
return $http.post("//...", thing)
.then(function (response) {
return response.data;
}, function (response) {
return $q.reject(response.statusText);
});
}
};
}]);
}());
I have been through a number of SO posts about this but each one seems to be a little different. First of all, if any of my service code is not correct in setting up the promise, please stop me there. I have been through about 10 different iterations of testing a rejected promise but nothing is working. Here is what I have:
beforeEach(inject(function ($injector,myService) {
sut = myService;
$httpBackend = $injector.get('$httpBackend');
$q = $injector.get('$q');
$rootScope = $injector.get('$rootScope');
dataToSend = "send me!";
deferred = $q.defer();
}));
it("should get error on error", function () {
var result,
expected = "FAIL!!!!!";
deferred.reject(expected);
$httpBackend.expectPOST(testUrl,dataToSend).respond(deferred.promise);
sut.createThing(dataToSend).then(function (returnFromPromise) {
result = returnFromPromise;
});
$rootScope.$apply();
$httpBackend.flush();
// expect something here but result is always wrong
});
I understand that promises run asynchronously... and I have been cobbling together tidbits of info, but does anyone have advice for completing this unit test?
Upvotes: 3
Views: 2171
Reputation: 14104
When you use the $httpBackend
service you don't need to work with promises in order to simulate HTTP requests. In fact, the respond()
method has the following signature:
respond: function(status, data, headers, statusText) {
...
}
To simulate an error all you have to do is return a non-success status code (!= 200):
it('should not create a thing', function() {
var result;
// Arrange
$httpBackend.expectPOST('https://domain.com/api')
.respond(500, null, null, 'some error');
// Act
myService.createThing('foo').then(angular.noop, function(data) {
result = data;
});
$httpBackend.flush();
// Assert
expect(result).toBe('some error');
});
And here's the whole spec covering both success and error cases:
describe('Testing a factory', function() {
var myService, $httpBackend;
beforeEach(function() {
module('plunker');
inject(function(_$httpBackend_, _myService_) {
$httpBackend = _$httpBackend_;
myService = _myService_;
})
});
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should create a thing', function() {
var result;
// Arrange
$httpBackend.expectPOST('https://domain.com/api')
.respond(200, 'some data');
// Act
myService.createThing('foo').then(function(data) {
result = data;
});
$httpBackend.flush();
// Assert
expect(result).toBe('some data');
});
it('should not create a thing', function() {
var result;
// Arrange
$httpBackend.expectPOST('https://domain.com/api')
.respond(500, null, null, 'some error');
// Act
myService.createThing('foo').then(angular.noop, function(data) {
result = data;
});
$httpBackend.flush();
// Assert
expect(result).toBe('some error');
});
});
Notice that there's no need to call $rootScope.$apply()
here, because no digest cycle is needed to update anything.
Upvotes: 3