Pete Pete - 5 months ago 6
AngularJS Question

AngularJs unit test - mocked promise not executing "then"

We're unit testing our controllers. We've successfully mocked the call to our REST service layer and verified that it is indeed being called with the given data. Now however we'd like to test that in our controller the execution of the

then
promise changes the
location.path
:

controller:

(function () {

app.controller('registerController', ['$scope', '$location', '$ourRestWrapper', function ($scope, $location, $ourRestWrapper) {

$scope.submitReg = function(){
// test will execute this
var promise = $ourRestWrapper.post('user/registration', $scope.register);

promise.then(function(response) {
console.log("success!"); // test never hits here
$location.path("/");
},
function(error) {
console.log("error!"); // test never hits here
$location.path("/error");
}
);
};


$ourRestWrapper.post(url,data)
just wraps
Restangular.all(url).post(data)
..

Our Test:

(function () {

describe("controller: registerController", function() {

var scope, location, restMock, controller, q, deferred;

beforeEach(module("ourModule"));

beforeEach(function() {
restMock = {
post: function(url, model) {
console.log("deferring...");
deferred = q.defer();
return deferred.promise;
}
};
});

// init controller for test
beforeEach(inject(function($controller, $rootScope, $ourRestWrapper, $location, $q){
scope = $rootScope.$new();
location = $location;
q = $q;

controller = $controller('registerController', {
$scope: scope, $location: location, $ourRestWrapper: restMock});
}));

it('should call REST layer with registration request', function() {
scope.register = {data:'test'};

spyOn(restMock, 'post').andCallThrough();

scope.submitReg();

deferred.resolve();

// successfull
expect(restMock.post).toHaveBeenCalledWith('user/registration',scope.register);
expect(restMock.post.calls.length).toEqual(1);
// fail: Expected '' to be '/'.
expect(location.path()).toBe('/');
});


In our console we see "deferring..." and the first two expectations succeed. Why will it not call the
then
block (i.e. set the location)?

Answer

Cache the $rootscope object when you get it from the injector and call $rootScope.$apply() immediately after deferred.resolve().