han4wluc
han4wluc

Reputation: 1199

AngularJS, Mocha, Karma. testing controller, mocking service promise

I'm using AngularJS, Karma, Mocha, Chai, Chai-as-promised, Sinon.
I'm trying the controller below. I am having trouble mocking the service User and test the .then() sections in the controller.

Controller Code

.controller('SignupPhoneCtrl', function(User, $scope) {
  $scope.sendPhoneNumber = function(countryCode, phoneNumber){
    User.sendPhoneNumber(countryCode, phoneNumber)
    .then(function(result){
      if(result == "success"){
        //I WANT TO TEST THIS SECTION
        return "success";
      }
        //I WANT TO TEST THIS SECTION
      return "fail";
    });
  }
})

ControllerTest Code

describe('Controller: Signup-phone', function() {
  var scope;
  var UserMock;
  var SignupPhoneCtrl;

  beforeEach(function() {
    module('starter.controllers');
  });

  beforeEach(inject(function($rootScope, $controller, $q){
    scope = $rootScope.$new();
    UserMock = {
      sendPhoneNumber: function(countryCode, phoneNumber){
        var deferred = $q.defer();
        // console.log("called"); //This part is being called
        deferred.resolve('foo');
        return deferred.promise;
      }
    };
    SignupPhoneCtrl = $controller("SignupPhoneCtrl", {$scope: scope, User: UserMock});
  }))

  it('should return asdf', function(){
    scope.$digest();

    //WHAT SHOULD I WRITE HERE?

  })
});


I have tried the below but the assertion part is not being called.

scope.sendPhoneNumber(12, 53452123).then(function(result){
  result.should.equal("fail");  //THIS SECTION IS NOT CALLED
})

I have tried the below, but it gives error: 'undefined' is not a function (evaluating 'promise.then.bind(promise)'

scope.sendPhoneNumber(12, 53452123).should.eventually.equal("fail");

sendPhoneNumber(12, 53452123) returns Object{$$state: Object{status: 0}}


I tried to add sinon.spy(UserMock, 'sendPhoneNumber'), but it makes no difference.


Upvotes: 4

Views: 1643

Answers (1)

Phil
Phil

Reputation: 165070

The problem is, $scope.sendPhoneNumber doesn't return anything. Try

return User.sendPhoneNumber(...

in your controller method.


Also, if you're just going to instantly resolve the promise in your mock, I've found this works nicely and saves you setting up a deferred object

UserMock = {
  sendPhoneNumber: function(countryCode, phoneNumber){
    return $q.when('foo');
  }
};

That being said, you probably want to be able to control the resolved value in each test so this makes more sense...

var scope;
var UserMock;
var SignupPhoneCtrl;
var deferred;

// snip

inject(function($rootScope, $controller, $q) {
    deferred = $q.defer();
    UserMock = {
        sendPhoneNumber: function() {
            return deferred.promise;
        }
    };
});

// snip

it('tests for success', inject(function($rootScope) {
    deferred.resolve('success');

    scope.sendPhoneNumber(...).then(...);

    $rootScope.$apply();
}));

it('tests for failure', inject(function($rootScope) {
    deferred.resolve('not success');

    scope.sendPhoneNumber(...).then(...);

    $rootScope.$apply();
}));    

Upvotes: 1

Related Questions