使用 Jasmine 刷新成功模拟 POST 请求不会执行 AngularJS 成功函数

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

这是我的 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;
            });
        };
    }]);

这是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;
    }]);

这是我的 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();
    });
});

现在,当我 运行 karma start,它 returns 这个:

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)

如你所见,应该会调用spy add,但是由于终端打印了Expected spy add to have been called.,按我的理解,这意味着它没有被调用,对吗?

此外,它还在终端上打印了 Expected '/post' to equal '/'.,所以这也意味着 URL 仍然在 '/post',对吗?

知道为什么 URL 没有改变并且没有调用 spy add 吗?

如果您正在测试控制器,测试控制器。这就是它被称为 单元测试 的原因。您应该模拟任何外部服务。

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