The reason this fails is that—although the code appears to be synchronous—internally $q
uses $evalAsync
from $scope
to defer the work until a future call stack. Since $q
has no flush
method like $httpBackend
, $timeout
, and $interval
do - a call to $rootScope.$digest()
is needed to achieve the same result.
PhantomJS 1.9.7 (Mac OS X): Executed 3 of 3 SUCCESS (0.451 secs / 0.01 secs)
Here is the updated example test;
describe('MyApp.myModule', function() {
describe('someService', function() {
beforeEach(function() {
var suite = this;
module('MyApp.myModule');
inject(function($rootScope) {
suite.$rootScope = $rootScope;
});
suite.injectService = function() {
inject(function(someService) {
suite.someService = someService;
});
};
});
describe('when instantiated', function() {
beforeEach(function() {
this.injectService();
});
it('should expose the expected API', function() {
expect(typeof this.someService.task).toEqual('function');
});
});
describe('someService.task', function() {
describe('when invoked', function() {
beforeEach(function() {
this.injectService();
this.taskPromise = this.someService.task();
});
it('should return a promise', function() {
expect(typeof this.taskPromise.then).toEqual('function');
});
describe('returned promise', function() {
describe('when invoked', function() {
beforeEach(function() {
this.onTaskComplete = jasmine.createSpy('onTaskComplete');
this.taskPromise.then(this.onTaskComplete);
this.$rootScope.$digest();
});
it('should call our handler immediately', function() {
expect(this.onTaskComplete).toHaveBeenCalledWith('some value');
});
});
});
});
});
});
});