Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS $timeout function not executing in my Jasmine specs

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?

like image 240
Esa Toivola Avatar asked Jun 27 '13 18:06

Esa Toivola


People also ask

What is the function of the $timeout service in AngularJS?

This '$timeout' service of AngularJS is functionally similar to the 'window. setTimeout' object of vanilla JavaScript. This service allows the developer to set some time delay before the execution of the function.

What is the function of the $timeout service?

The $timeout service can be used to call another JavaScript function after a given time delay. The $timeout service only schedules a single call to the function. For repeated calling of a function, see $interval later in this text.

How do you reset a $timeout and disable a watch ()?

var customTimeout = $timeout(function () { // arbitrary code }, 55); $timeout. cancel(customTimeout); The same applies to “$interval()”. To disable a watch, just call it.

What is the difference between $timeout and setTimeout?

setTimeout in order to display an alert message after a timeout of at least 2000 milliseconds. Angular $timeout is a wrapper written for window. setTimeout in form of a try catch block which throws exceptions via $exceptionHandler service. $timeout accepts the function to be delayed, delay time, a boolean to invoke $.


2 Answers

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.

like image 160
rtcherry Avatar answered Oct 11 '22 22:10

rtcherry


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.

like image 37
stoverben Avatar answered Oct 11 '22 21:10

stoverben