未注入模拟服务,但正在注入实际服务

Mock service is not being injected but actual service is being injected

我正在尝试测试使用服务的控制器。当我 运行 在注入函数中进行以下测试和断点时,将注入实际服务而不是模拟服务。为什么通过 $provider.factory 定义的服务没有被注入?

"use strict";

describe("contestantController", function () {
    var dataService, rootScope, scope, passPromise, contestantController;

    beforeEach(function(){
        module(function ($provide) {

            //mock service
            $provide.factory('contestantDataService', ['$q', function ($q) {
                function save(data){
                    if(passPromise){
                        return $q.when();
                    } else {
                        return $q.reject();
                    }
                }
                function getData() {
                    if (passPromise) {
                        return $q.when(smallDataSet());
                    } else {
                        return $q.reject();
                    }
                }
                return {
                    addContestant: save,
                    getContestants: getData,
                };
            }]);

        });

        module('contestantApp');
    });

    beforeEach(inject(function ($rootScope, $controller, contestantDataService) {
        rootScope = $rootScope;
        scope = $rootScope.$new();
        dataService = contestantDataService;

        spyOn(dataService, 'getContestants').and.callThrough();

        contestantController = $controller('contestantController', {
            $scope: scope,
            contestantDataService: dataService
        });
    }));

    it('should call getContestants method on contestantDataService on calling saveData', function () {

        passPromise = true;
        rootScope.$digest();

        expect(dataService.getContestants).toHaveBeenCalled();
        expect(scope.contestants).toEqual(smallDataSet());
    });

});

感谢@Yunchi。你指出了我的错误。

您应该在模拟服务之前调用模块函数 contestantDataService

只需将您的代码更新为这种方式,

//make sure contesttantDataService exist in this module in your production environment.

var dataService, rootScope, scope, passPromise, contestantController;
beforeEach(function(){
    //Add module name here which means we define the mockService under this module
    module('contestantApp', function ($provide) {
        //mock service
        $provide.factory('contestantDataService', ['$q', function ($q) {
            function save(data){
                if(passPromise){
                    return $q.when();
                } else {
                    return $q.reject();
                }
            }
            function getData() {
                if (passPromise) {
                    return $q.when(smallDataSet());
                } else {
                    return $q.reject();
                }
            }
            return {
                addContestant: save,
                getContestants: getData,
            };
        }]);
    });
    beforeEach(inject(function ($rootScope, $controller, contestantDataService) {
         rootScope = $rootScope;
         scope = $rootScope.$new();
         dataService = contestantDataService;

         spyOn(dataService, 'getContestants').and.callThrough();
         //no need to manually inject to our controller now.
         contestantController = $controller('contestantController', {
             $scope: scope
     });
}));

我认为在我们的单元测试中使用 $provide.value 比使用 $provide.factory 更好。因为我们只需要确保 contestantDataService 是一个对象并具有功能即可。

您可以查看以下类似问题,

Injecting a mock into an AngularJS service.

Mock a service in order to test a controller.

Here is the jsfiddle I just created.

玩得开心。 :)

Tyler 的回答可以,但推理有点不对。

module() 函数简单地注册模块或模块初始化函数,inject() 函数稍后将使用这些模块或模块初始化函数来初始化 Angular 注入器。它决不会将模拟服务与模块链接起来。

您的代码不起作用,因为注册服务的顺序很重要。您的代码首先注册一个 mock contestantDataService,然后注册 contestantApp 模块,其中包含自己的 contestantDataService,覆盖已注册的 mock。如果您只是将 module('contestantApp') 调用移动到顶部,它应该会按预期工作。

所以这意味着下面的两个块是等效的并且都可以工作...

beforeEach(function(){
  module('contestantApp');
  module(function ($provide) {
    ...
  });
);

beforeEach(function(){
  module('contestantApp', function ($provide) {
    ...
  });
);