SilentDev
SilentDev

Reputation: 22747

Flushing successful mock POST request using Jasmine does not execute the AngularJS success function

This is my AngularJS post.js:

angular.module("PostPageApp", ["BaseApp"])
    .controller("MainCtrl", ["$http", "$window", "BaseService", function($http, $window, BaseService) {

        var self = this;

        self.add = function() {
            BaseService.add.post(self.post, function() {
                self.cerrorMessages = BaseService.cerrorMessages;
            });
        };
    }]);

This is base.js:

angular.module("BaseApp", [])
    .config(['$httpProvider', function($httpProvider) {
        $httpProvider.defaults.xsrfCookieName = 'csrftoken';
        $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
    }])

    .config(['$locationProvider', function($locationProvider){
        $locationProvider.html5Mode(true);
    }])

    .factory("BaseService", ["$http", "$window", function($http, $window) {
        var self = this;
        self.posts = [];
        self.cerrorMessages = [];

        /* This function sets self.cerrorMessages. After calling this function,
         * you should do a callback to a function on the front-end which
         * sets cerrorMessage. */
        self.accessErrors = function(data) {
             self.cerrorMessages = [];
             for (prop in data) {
                 if (data.hasOwnProperty(prop)){
                     /* if (data[prop] != null && data[prop].constructor ==  Object) {
                         self.accessErrors(data[prop]);
                     }
                     else { */
                     self.cerrorMessages.push(data[prop]);
                     // }
                 }
             }
         };

        self.add = {
            post: function(post, callback) {
                $http.post("/posts/", post)
                .then(function(response) {
                    $window.location.href = "/";
                }, function(response) {
                    self.accessErrors(response.data);
                    callback();
                });
            }
         };

        return self;
    }]);

And this is my test_post.js:

describe('Controller: MainCtrl', function() {
    beforeEach(module('PostPageApp'));

    var ctrl, $loc;

    beforeEach(inject(function($controller, $location, $httpBackend, BaseService) {
        ctrl = $controller('MainCtrl');
        $loc = $location;
        mockBackend = $httpBackend;

        spyOn(BaseService, 'add').and.callThrough();
        baseService = BaseService;
    }));

    it('should have an add function', function() {
        expect(ctrl.add).toBeDefined();
    });

    it('should be able to create a post object', function() {
        $loc.path('/post');
        ctrl.post = {'post':'Test post'}
        mockBackend.expectPOST('/posts/', ctrl.post)
            .respond(201, {'post':'TestPost', 'posting': 1});

        ctrl.add();

        mockBackend.flush();
        expect(baseService.add).toHaveBeenCalled();
        expect($loc.path()).toEqual('/');
        expect(ctrl.cerrorMessages).toBeUndefined();
    });
});

Now, when I run karma start, it returns this:

Chromium 47.0.2526 (Ubuntu 0.0.0) Controller: MainCtrl should be able to create a valid post object FAILED
    Expected spy add to have been called.
        at Object.<anonymous> (/home/u/Documents/CMS/CMSApp/static/js/karma/tests/test_post.js:32:33)
    Expected '/post' to equal '/'.
        at Object.<anonymous> (/home/u/Documents/CMS/CMSApp/static/js/karma/tests/test_post.js:33:29)
Chromium 47.0.2526 (Ubuntu 0.0.0): Executed 3 of 3 (1 FAILED) (0 secs / 0.119 secChromium 47.0.2526 (Ubuntu 0.0.0): Executed 3 of 3 (1 FAILED) (0.163 secs / 0.119 secs)

As you can see, spy add was expected to be called, but since Expected spy add to have been called. was printed on the terminal, from my understanding, this means that it was not called, right?

Also, it also printed Expected '/post' to equal '/'. onto the terminal so this also means that the URL is still at '/post', correct?

Any idea why the URL did not change and spy add was not called?

Upvotes: 3

Views: 1628

Answers (1)

Phil
Phil

Reputation: 164793

If you're testing the controller, only test the controller. That's why it's called a unit test. You should be mocking any external services.

describe('Controller: MainCtrl', function() {
    var ctrl, mockBaseService;

    beforeEach(function() {
        mockBaseService = {
            cerrorMessages: 'whatever',
            add: jasmine.createSpyObj('BaseService.add', ['post'])
        };

        mockBaseService.add.post.and.callFake(function(something, cb) {
            cb(); // execute the callback immediately
        });

        module('PostPageApp');

        inject(function($controller) {
            ctrl = $controller('MainCtrl', {
                BaseService: mockBaseService
            });
        });
    });

    it('add calls through to BaseService.add.post', function() {
        ctrl.post = 'something'; // just adding this because I can't see it anywhere else

        ctrl.add();

        expect(mockBaseService.add.post).toHaveBeenCalledWith(ctrl.post, jasmine.any(Function));
        expect(ctrl.cerrorMessages).toBe(mockBaseService.cerrorMessages);
    });
});

Upvotes: 3

Related Questions