Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I resolve a promise in an AngularJS unit test

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?

like image 692
jhamm Avatar asked Feb 08 '14 13:02

jhamm


People also ask

How to test if a promise is resolved in angular?

The catch is though, that you need to call rootScope.$apply () method manually in your test as promises only get resolved during a angular $digest phase. After this method is finished, all promises are really resolved and you can test for the expected outcome.

Should promises be used in unit tests?

Ends Soon Promises are becoming a common part of the JavaScript code. The native Promise object is already supported by all the major browsers including Chrome, Firefox, and Safari. Despite making asynchronous code simpler, dealing with promises in unit tests is a hassle.

What happens if a promise is rejected in testing?

In this case, if the promise was rejected, there would be no error, since there is no error handler in the test to check for it. But it’s clear the test should fail in that situation, as the expectation won’t run. This is definitely one of the main reasons why promises become complicated in tests.

What is the use of loaddata () method in angular?

This method is calling loadData () on an angularJS service which is returning a promise, which could be resolved only after a $http call is finished. How to handle that in a unit test?


2 Answers

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.

like image 66
Bergi Avatar answered Oct 23 '22 20:10

Bergi


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!

like image 27
nackjicholson Avatar answered Oct 23 '22 20:10

nackjicholson