39

我正在模拟 AngularJS 单元测试的服务。我正在使用该$provide服务将“真实”服务替换为模拟出来的服务(可用的plunker 脚本):

describe('My Controller', function () {

    var $scope;
    var $provide;

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

    beforeEach(angular.mock.module(function (_$provide_) {

        $provide = _$provide_;

    }));

    beforeEach(angular.mock.inject(function($rootScope, $controller, $q){

        var mockMyService = {
            getAll : function() {
                var deferred = $q.defer();
                deferred.resolve([
            { itemText: "Foo" },
            { itemText: "Bar" }
                ]);

                return deferred.promise;
            }
        };

        $provide.value('myService', mockMyService);

        $scope = $rootScope.$new();

        $controller('MyCtrl', { $scope: $scope });

        $rootScope.$apply();

    }));

    it('Has two items defined', function () {
        expect($scope.items.length).toEqual(2);
    });
});

这工作得很好。但是,我不喜欢我使用一个angular.mock.module函数只是为了引用该$provide服务,然后在angular.mock.inject下面的函数中使用该服务。但是,如果我直接将$provide其作为参数添加到angular.mock.inject函数中,则会出现“未知提供者”错误。

我突然想到我可以将所有模拟代码放在angular.mock.module函数中。但是我对$q引用有类似的问题,我需要它,因为我的模拟服务必须返回一个承诺。

换句话说,如果我$q向函数添加参数,angular.mock.module那么我也会收到“未知提供者”错误。

有没有办法简化这个?显然我有什么作品,但不知何故感觉不太对劲。我觉得我缺乏理解为什么有些提供者在inject函数中可用,而另一些在module函数中可用。

4

2 回答 2

55

您不能$provideinject函数内使用,因为前者注册提供者供后者使用。看一看:

describe('...', function() {
    beforeEach(function() {
        module(function($provide) {
            $provide.constant('someValue', 'foobar');
        });

        inject(function(someValue) {
            var value = someValue; // will be 'foobar';
        });
    });
});

你可以这样写你的测试:

describe('...', function() {
    var serviceMock;

    beforeEach(function() {
        serviceMock = {
           someMethod: function() { ... }
        };

        module(function($provide) {
            $provide.value('service', serviceMock);
        });

        inject(function(service) {
            ...                         
        });
    });
});

事实上,你甚至不需要在注入模拟服务之前实现它$provide

beforeEach(function() {
    serviceMock = {};

    module(function($provide) {
        $provide.value('service', serviceMock);
    });

    inject(function(service) {
        ...                         
    });
});

it('tests something', function() {
    // Arrange
    serviceMock.someMethod = function() { ... }

    // Act
    // does something

    // Assert
    expect(...).toBe(...);
});

这是一个Plunker 脚本,主要说明了上述内容。

于 2013-10-10T16:52:35.860 回答
12

当我不得不包装使用$q并且看起来很干净的服务时,这对我有用:

var _ServiceToTest_;
beforeEach(function () {
    module('module.being.tested');
    module(function ($provide) {
        $provide.factory('ServiceToMock', function ($q, $rootScope) {
            var service = ...;
            // use $q et al to heart's content
            return service;
        });
    });
    inject(function (_ServiceToTest_) {
        ServiceToTest = _ServiceToTest_;
    });
});

it('...', function () { /* code using ServiceToTest */ });

诀窍是使用$provide.factory而不是$provide.value.

于 2015-11-20T23:16:44.180 回答