4
votes

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.


1

1 Answers

1
votes

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();
}));