Reputation: 12750
I'm trying to unit test a factory:
angular.module('app.user').factory('UserService',
['RESTService', 'RESTHTTPService',
function(RESTService, RESTHTTPService) {
var factory = {};
factory.search = function(searchTerm, page, size, sort, callback) {
return RESTService.User.search({searchTerm: searchTerm, page: page, size: size, sort: sort}).$promise.then(callback);
};
return factory;
}
]);
So I needed to mock its RESTService dependency.
angular.module('app.user.mock').factory('RESTService',
['$q',
function($q) {
var factory = { User: {} };
factory.allUsers = {
content: [ { firstname: 'Spirou', lastname: 'Fantasio', email: '[email protected]', workPhone: '983743464365' } ],
page: '1'
};
factory.User.search = function() {
var defer = $q.defer();
defer.resolve(factory.allUsers);
return defer;
};
return factory;
}
]);
To be used in my unit test:
describe('user service repository', function() {
beforeEach(function() {
module('com.nsn.nitro.project');
module('app.utils.mock');
});
var UserService;
var RESTService;
beforeEach(inject(function(_UserService_, _RESTService_) {
UserService = _UserService_;
RESTService = _RESTService_;
}));
it('should return the list of users', function () {
var users = null;
UserService.search('TOTO', 1, 10, 'asc', function(data) {
users = data.content;
});
expect(users).toEqual(RESTService.allUsers.content);
});
});
But it gives me the error:
TypeError: Cannot read property 'then' of undefined
at Object.factory.all (/home/stephane/dev/js/projects/angularjs/nitro-project/app/modules/user/service/repository.js:29:81)
at null.<anonymous> (/home/stephane/dev/js/projects/angularjs/nitro-project/test/spec/modules/user/service.js:59:17)
The mocked RESTService is injected fine in the UserService but the promise property, or rather the $promise property, is undefined.
As the code works fine in production, I would like not to touch the existing UserService and RESTService factories. If possible.
Upvotes: 0
Views: 146
Reputation: 12750
To get the test to pass I needed to do two things:
1- Add a $rootScope.$digest(); right after the service call:
it('should return the list of searched users', function() {
var users = null;
UserService.search('TOTO', 1, 10, 'asc', function(data) {
users = data.content;
});
$rootScope.$digest();
expect(users).toEqual(RESTService.allUsers.content);
});
2- Add $httpBackend.whenGET(/.html$/).respond(''); to work around the issue of httpBackend generating a call to the ui-router template. I'm discussing this issue in another thread at How to avoid the 'Error: Unexpected request: GET' every time an AngularJS test does a rootScope.digest() or httpBackend.flush()
beforeEach(inject(function(_$httpBackend_, _$rootScope_, _UserService_, _RESTService_) {
$httpBackend = _$httpBackend_;
$rootScope = _$rootScope_;
UserService = _UserService_;
RESTService = _RESTService_;
// Always use this statement so as to avoid the error from the $http service making a request to the application main page
$httpBackend.whenGET(/\.html$/).respond('');
}));
Upvotes: 0
Reputation: 5825
$q's promise doens't have a property named $promise. You can set a one with a reference to the promise
property:
factory.User.get = function() {
var defer = $q.defer();
defer.resolve(this.allUsers);
defer.$promise = defer.promise;
return defer;
};
Also, because the test is async, you might need to use jasmine's done parmater (depends on your version):
it('should return the list of users', function (done) {
var users = null;
UserService.all(1, 10, 'asc', function(data) {
users = data.content;
expect(users).toEqual(RESTService.allUsers.content);
done();
});
});
Upvotes: 1