Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing AngularJS promises in Jasmine 2.0

I've been trying to wrap my head around Jasmine 2.0 and AngularJS promises. I know that:

  • Jasmine 2.0 introduced the done function to replace the old runs and waitsFor functions
  • AngularJS $q promises will not resolve until a digest cycle is triggered

How can I test AngularJS promises using the new async syntax in Jasmine 2.0?

like image 765
Tyler Eich Avatar asked Apr 17 '14 11:04

Tyler Eich


People also ask

How to do unit testing in AngularJS?

Testing in AngularJS is achieved by using the karma framework, a framework which has been developed by Google itself. The karma framework is installed using the node package manager. The key modules which are required to be installed for basic testing are karma, karma-chrome-launcher ,karma-jasmine, and karma-cli.

What is promise in Jasmine?

Promises. If you can't use async / await or you need more control, you can explicitly return a promise instead. Jasmine considers any object with a then method to be a promise, so you can use either the Javascript runtime's built-in Promise type or a library.


2 Answers

After your call to promise.resolve():

  • Call $timeout.flush(). This will force a digest cycle and propagate the promise resolution
  • Call done(). This tells Jasmine the async tests have completed

Here's an example (Demo on Plunker):

describe('AngularJS promises and Jasmine 2.0', function() {     var $q, $timeout;      beforeEach(inject(function(_$q_, _$timeout_) {         // Set `$q` and `$timeout` before tests run         $q = _$q_;         $timeout = _$timeout_;     }));      // Putting `done` as argument allows async testing     it('Demonstrates asynchronous testing', function(done) {         var deferred = $q.defer();          $timeout(function() {             deferred.resolve('I told you I would come!');         }, 1000); // This won't actually wait for 1 second.                   // `$timeout.flush()` will force it to execute.          deferred.promise.then(function(value) {             // Tests set within `then` function of promise             expect(value).toBe('I told you I would come!');         })         // IMPORTANT: `done` must be called after promise is resolved         .finally(done);          $timeout.flush(); // Force digest cycle to resolve promises     }); }); 
like image 149
Tyler Eich Avatar answered Sep 30 '22 19:09

Tyler Eich


For me the $timeout.flush() didn't work very well, but I've multiple async calls in my spec. I found the $rootScope.$apply(), as a method to force the digeston each async call.

describe('AngularJS promises and Jasmine 2.0', function () {   beforeEach(inject(function (_$q_, _$timeout_, _$rootScope_) {     $q = _$q_     $timeout = _$timeout_     $rootScope = _$rootScope_   }))    it('demonstrates asynchronous testing', function (done) {     var defer = $q.defer()      Async.call()     .then(function (response) {       // Do something        var d = $q.defer()       Async.call()       .then(function (response) {         d.resolve(response)         $rootScope.$apply() // Call the first digest        })       return d.promise     })     .then(function (response) {       // Do something after the first digest        Async.call()       .then(function (response) {         defer.resolve(response) // The original defer         $rootScope.$apply() // Call the second digest       })     })      defer.promise.then(function(value) {       // Do something after the second digest       expect(value).toBe('I told you I would come!')     })     .finally(done)      if($timeout.verifyNoPendingTasks())       $timeout.flush()    }) }) 

It is like a chained async calls thing. Hope it helps the conversation. Regards

like image 36
mariowise Avatar answered Sep 30 '22 19:09

mariowise