Reputation: 25022
I am using the basic karma/jasmine setup to test my Angular code. Here is my test:
var $controllerConstructor, ctr, mockSuperheroData, scope, deferred, q;
describe('main controller', function() {
var $controllerConstructor, ctr, mockSuperheroData, scope, deferred, q;
beforeEach(inject(function($controller, $rootScope, $q) {
scope = $rootScope.$new();
$controllerConstructor = $controller;
q = $q;
mockSuperheroData = {
getSuperheroes: function() {
deferred = q.defer();
return deferred.promise;
}
};
ctr = $controllerConstructor('MainCtrl', {$scope: scope, $location: {}, superheroService: mockSuperheroData, keys: {}});
}));
it('should set the result of getResource to scope.heroes', function() {
scope.getHeroes();
expect(scope.heroes).toBe(100);
});
}
scope.getHeroes()
calls the mockSuperheroData.getSuperheroes()
which is returning a promise. How do I force the promise to return what I want in the unit test? Where can I hook into the promise to mock out its return?
Upvotes: 6
Views: 5264
Reputation: 4839
var $controllerConstructor, ctr, mockSuperheroData, scope, deferred, q;
describe('main controller', function() {
var $controllerConstructor, ctr, mockSuperheroData, scope, deferred, q, rootScope;
beforeEach(inject(function($controller, $rootScope, $q) {
scope = $rootScope.$new();
$controllerConstructor = $controller;
q = $q;
rootScope = $rootScope;
mockSuperheroData = {
getSuperheroes: function() {
deferred = q.defer();
return deferred.promise;
}
};
ctr = $controllerConstructor('MainCtrl', {$scope: scope, $location: {}, superheroService: mockSuperheroData, keys: {}});
}));
it('should set the result of getResource to scope.heroes', function() {
scope.getHeroes();
deferred.resolve(100);
rootScope.$apply();
expect(scope.heroes).toBe(100);
});
});
It should be mentioned that because $q is integrated with $rootScope, not only do you need to resolve the deferred object, but you also need to propagate the changes by calling $rootScope.$apply.
I wrote a blog on mocking angular promises at projectpoppycock
and I wrote a working example, with tests, at plunkr
I really like the way you're injecting the mock service as a dependency of the controller by using the $controller service, I may need to revise my way of doing this. Your way is better :) Thanks!
Upvotes: 2
Reputation: 664196
How do I force the promise to return what I want in the unit test?
Basically you will need to call resolve
on the Deferred:
deferred.resolve(100);
You can either put that directly before the return deferred.promise
or in an asynchronous setTimeout
.
Upvotes: 4