6

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?

2
  • You really could use some var declarations… Commented Feb 8, 2014 at 13:29
  • Sorry, I forgot to add them to the question, but now they are there. Commented Feb 8, 2014 at 13:29

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

Comments

2
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!

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.