单元测试 spyOn 永远不会被称为 angular 工厂

Unit test spyOn is never called angular factory

我正在尝试为 angular 工厂编写单元测试:

define([], function () {
    'use strict';

    var factoryName = 'UserFactory';
    var components = ['ProductFactory', '$state'];

    var factory = function (ProductFactory, $state) {

        var viewState = function(view, data) {
            if(data.access[view] === true) {
                return;
            }
            else {                
                $state.go('notAuth');
            }
        };

        return {
            viewState: viewState
        };
    };

    components.push(factory);

    return {
        name: factoryName,
        component: components
    };
});

我有以下单元测试:

define(['require', 'angular-mocks'], function (require) {
    'use strict';

    var angular = require('angular');

    describe('<-- UserFactory Spec ------>', function () {

        var scope, $httpBackend, userFactory;

        beforeEach(angular.mock.module('UserFactory'));

        beforeEach(inject(function (_$rootScope_, _UserFactory_) {
            scope = _$rootScope_.$new();
            userFactory = _UserFactory_;
            spyOn(userFactory, 'viewState').and.returnValue({firstName:'Joe',lastName:'Smith',access:'read'});
            scope.$apply();
        }));


        it('method viewState() was called', function () {
            expect(userFactory.viewState).toHaveBeenCalled();
        });
    });
});

但是我看到以下错误:

Expected spy viewState to have been called.

如果您尝试测试 viewState 函数的实现,则需要在测试中调用它。您也不需要测试它是否被调用(因为源代码没有测试它)。

你不需要模拟它,这违背了测试它的目的。这是因为模拟 不会 调用实际的实现,除非你告诉它但你在这里不需要它。

您的测试应如下所示:

describe('<-- UserFactory Spec ------>', function () {

    var userFactory;
    var $state = {
        go: function(){}
    };

    beforeEach(angular.mock.module('UserFactory'));

    // Set the $state injectable to be our mocked out $state (only for this test)
    beforeEach(module(function ($provide) {
        $provide.value('$state', $state);
    }));

    beforeEach(inject(function (_UserFactory_) {
        userFactory = _UserFactory_;

        // Mock out the $state.go method
        spyOn($state, 'go'); 
    }));


    it('should redirect to the notAuth state', function () {
        // Call the method we are testing
        userFactory.viewState(/** pass in some conditions to make it call $state.go**/);
        expect($state.go).toHaveBeenCalledWith('notAuth');
    });
});

我在这里做了一些事情:

  • 删除了 $scope,这里不需要它。
  • 模拟了 $state.go function - 所以我们可以测试它被调用了。
  • 已调用 userFactory.viewState() - 因此我们可以测试函数。
  • 删除了 viewState() 的模拟 - 这样我们就可以测试实际的实现。

要测试它是否正确调用此方法,您可以使用一些 console.logs.

var viewState = function(view, data) {
    console.log('View ', view);
    console.log('Data ', data);
    console.log(data.access[view]);
    console.log(data.access[view] === true);
    if(data.access[view] === true) {
        return;
    }
    else {                
        $state.go('notAuth');
    }
};

现在只需检查所有控制台日志是否符合您的预期。


P.S 我无法测试此代码,因此它可能不是 100%,但这是一般要点。