AngularJS $timeout function not executing in my Jasmine specs
Solution 1
According to the Angular JS documentation for $timeout, you can use $timeout.flush()
to synchronously flush the queue of deferred functions.
Try updating your test to this:
it('should do stuff', function() {
expect($scope.stuffDone).toBeFalsy();
$scope.doStuff();
expect($scope.stuffDone).toBeFalsy();
$timeout.flush();
expect($scope.stuffDone).toBeTruthy();
});
Here is a plunker showing both your original test failing and the new test passing.
Solution 2
As noted in one of the comments, Jasmine setTimeout
mock is not being used because angular's JS mock $timeout
service is used instead. Personally, I'd rather use Jasmine's because its mocking method lets me test the length of the timeout. You can effectively circumvent it with a simple provider in your unit test:
module(function($provide) {
$provide.constant('$timeout', setTimeout);
});
Note: if you go this route, be sure to call $scope.apply()
after jasmine.Clock.tick.
Solution 3
As $timeout
is just a wrapper for window.setTimeout
, you can use jasmines Clock.useMock()
which mocks the window.setTimeout
beforeEach(function() {
jasmine.Clock.useMock();
});
it('should do stuff', function() {
$scope.doStuff();
jasmine.Clock.tick(251);
expect($scope.stuffDone).toBeTruthy();
});
Comments
-
Esa Toivola almost 2 years
I'm trying to test my AngularJS controller with Jasmine, using Karma. But a
$timeout
which works well in real-life, crashes my tests.Controller:
var Ctrl = function($scope, $timeout) { $scope.doStuff = function() { $timeout(function() { $scope.stuffDone = true; }, 250); }; };
Jasmine it block (where
$scope
and controller have been properly initialized):it('should do stuff', function() { runs(function() { $scope.doStuff(); }); waitsFor(function() { return $scope.stuffDone; }, 'Stuff should be done', 750); runs(function() { expect($scope.stuffDone).toBeTruthy(); }); });
When I run my app in browser,
$timeout
function will be executed and$scope.stuffDone
will be true. But in my tests,$timeout
does nothing, the function is never executed and Jasmine reports error after timing out 750 ms. What could possibly be wrong here? -
Esa Toivola about 11 yearsThanks. Should've RTFM. It seems that in Jasmine tests ngMocks module is loaded and the mocked $timeout actually never calls window.setTimeout - am I correct?
-
rtcherry about 11 years
-
nabeelfarid over 10 yearsHow do I reverse it back to the angular timeout mock, as I have other tests where I don't want to use jasmine mock?
-
chas s. about 10 yearsThis is for the Jasmine 1.x API. For Jasmine 2.x you would run
jasmine.clock().install()
in yourbeforeEach
,jasmine.clock().uninstall()
in yourafterEach
andjasmine.clock().tick(251)
in yourit
function. -
rtcherry over 9 years
-
HipsterZipster almost 9 yearsIs it possible to switch back to using timeout.flush(1000) in any of those tests? It seems that using jasmine.clock().tick(1000) isn't a perfect substitute in some cases.
-
Basavaraj Sonnad over 5 yearsThis helps. But i had to call done() in the end to mark end of test case. Otherwise jasmine waits for 5sec(default) and fails as done() is not called. Hope this helps