Pete
Pete

Reputation: 10918

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)?

Upvotes: 11

Views: 4372

Answers (1)

Pete Martin
Pete Martin

Reputation: 856

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

Upvotes: 24

Related Questions