Javascript 将模拟注入 AngularJS 服务

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14773269/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 17:51:40  来源:igfitidea点击:

Injecting a mock into an AngularJS service

javascriptangularjsmockingjasmineangularjs-service

提问by BanksySan

I have an AngularJS service written and I would like to unit test it.

我编写了一个 AngularJS 服务,我想对其进行单元测试。

angular.module('myServiceProvider', ['fooServiceProvider', 'barServiceProvider']).
    factory('myService', function ($http, fooService, barService) {

    this.something = function() {
        // Do something with the injected services
    };

    return this;
});

My app.js file has these registered:

我的 app.js 文件注册了这些:

angular
.module('myApp', ['fooServiceProvider','barServiceProvider','myServiceProvider']
)

I can test the DI is working as such:

我可以测试 DI 是否正常工作:

describe("Using the DI framework", function() {
    beforeEach(module('fooServiceProvider'));
    beforeEach(module('barServiceProvider'));
    beforeEach(module('myServiceProvder'));

    var service;

    beforeEach(inject(function(fooService, barService, myService) {
        service=myService;
    }));

    it("can be instantiated", function() {
        expect(service).not.toBeNull();
    });
});

This proved that the service can be created by the DI framework, however next I want to unit test the service, which means mocking out the injected objects.

这证明服务可以由 DI 框架创建,但是接下来我想对服务进行单元测试,这意味着模拟注入的对象。

How do I go about doing this?

我该怎么做?

I've tried putting my mock objects in the module, e.g.

我试过将我的模拟对象放在模块中,例如

beforeEach(module(mockNavigationService));

and rewriting the service definition as:

并将服务定义重写为:

function MyService(http, fooService, barService) {
    this.somthing = function() {
        // Do something with the injected services
    };
});

angular.module('myServiceProvider', ['fooServiceProvider', 'barServiceProvider']).
    factory('myService', function ($http, fooService, barService) { return new MyService($http, fooService, barService); })

But the latter seems to stop the service being created by the DI as all.

但后者似乎停止了由 DI 创建的服务。

Does anybody know how I can mock the injected services for my unit tests?

有人知道我如何为我的单元测试模拟注入的服务吗?

Thanks

谢谢

David

大卫

回答by John Galambos

You can inject mocks into your service by using $provide.

您可以使用$provide.

If you have the following service with a dependency that has a method called getSomething:

如果您有以下具有依赖项的服务,该服务具有名为 getSomething 的方法:

angular.module('myModule', [])
  .factory('myService', function (myDependency) {
        return {
            useDependency: function () {
                return myDependency.getSomething();
            }
        };
  });

You can inject a mock version of myDependency as follows:

您可以按如下方式注入 myDependency 的模拟版本:

describe('Service: myService', function () {

  var mockDependency;

  beforeEach(module('myModule'));

  beforeEach(function () {

      mockDependency = {
          getSomething: function () {
              return 'mockReturnValue';
          }
      };

      module(function ($provide) {
          $provide.value('myDependency', mockDependency);
      });

  });

  it('should return value from mock dependency', inject(function (myService) {
      expect(myService.useDependency()).toBe('mockReturnValue');
  }));

});

Note that because of the call to $provide.valueyou don't actually need to explicitly inject myDependency anywhere. It happens under the hood during the injection of myService. When setting up mockDependency here, it could just as easily be a spy.

请注意,由于对$provide.value您的调用实际上并不需要在任何地方显式注入 myDependency。它在注入 myService 期间发生在幕后。在此处设置 mockDependency 时,它很容易成为间谍。

Thanks to loyalBrownfor the link to that great video.

感谢loyalBrown提供该精彩视频的链接。

回答by dnc253

The way I look at it, there's no need to mock the services themselves. Simply mock the functions on the service. That way, you can have angular inject your real services as it does throughout the app. Then, mock the functions on the service as needed using Jasmine's spyOnfunction.

在我看来,没有必要嘲笑服务本身。只需模拟服务上的功能。这样,您就可以像在整个应用程序中一样使用 angular 注入您的真实服务。然后,根据需要使用 Jasmine 的spyOn函数模拟服务上的函数。

Now, if the service itself is a function, and not an object that you can use spyOnwith, there's another way to go about it. I needed to do this, and found something that works pretty well for me. See How do you mock Angular service that is a function?

现在,如果服务本身是一个函数,而不是一个您可以使用的对象,那么spyOn还有另一种方法来处理它。我需要这样做,并找到了一些对我很有效的东西。请参阅如何模拟作为函数的 Angular 服务?

回答by tennisgent

Another option to help make mocking dependencies easier in Angular and Jasmine is to use QuickMock. It can be found on GitHub and allows you to create simple mocks in a reusable way. You can clone it from GitHub via the link below. The README is pretty self explanatory, but hopefully it might help others in the future.

在 Angular 和 Jasmine 中帮助简化模拟依赖项的另一个选择是使用 QuickMock。它可以在 GitHub 上找到,并允许您以可重用的方式创建简单的模拟。您可以通过以下链接从 GitHub 克隆它。README 是非常自我解释的,但希望它将来可以帮助其他人。

https://github.com/tennisgent/QuickMock

https://github.com/tennisgent/QuickMock

describe('NotificationService', function () {
    var notificationService;

    beforeEach(function(){
        notificationService = QuickMock({
            providerName: 'NotificationService', // the provider we wish to test
            moduleName: 'QuickMockDemo',         // the module that contains our provider
            mockModules: ['QuickMockDemoMocks']  // module(s) that contains mocks for our provider's dependencies
        });
    });
    ....

It automatically manages all of the boilerplate mentioned above, so you don't have to write out all of that mock injection code in every test. Hope that helps.

它会自动管理上面提到的所有样板,因此您不必在每个测试中写出所有模拟注入代码。希望有帮助。

回答by Ignitor

In addition to John Galambos' answer: if you just want to mock out specific methods of a service, you can do it like this:

除了John Galambos 的回答:如果您只想模拟服务的特定方法,您可以这样做:

describe('Service: myService', function () {

  var mockDependency;

  beforeEach(module('myModule'));

  beforeEach(module(function ($provide, myDependencyProvider) {
      // Get an instance of the real service, then modify specific functions
      mockDependency = myDependencyProvider.$get();
      mockDependency.getSomething = function() { return 'mockReturnValue'; };
      $provide.value('myDependency', mockDependency);
  });

  it('should return value from mock dependency', inject(function (myService) {
      expect(myService.useDependency()).toBe('mockReturnValue');
  }));

});

回答by CodingWithSpike

If your controller is written to take in a dependency like this:

如果您的控制器被编写为接受这样的依赖项:

app.controller("SomeController", ["$scope", "someDependency", function ($scope, someDependency) {
    someDependency.someFunction();
}]);

then you can make a fake someDependencyin a Jasmine test like this:

那么你可以someDependency像这样在 Jasmine 测试中做一个假:

describe("Some Controller", function () {

    beforeEach(module("app"));


    it("should call someMethod on someDependency", inject(function ($rootScope, $controller) {
        // make a fake SomeDependency object
        var someDependency = {
            someFunction: function () { }
        };

        spyOn(someDependency, "someFunction");

        // this instantiates SomeController, using the passed in object to resolve dependencies
        controller("SomeController", { $scope: scope, someDependency: someDependency });

        expect(someDependency.someFunction).toHaveBeenCalled();
    }));
});

回答by Emil van Galen

I recently released ngImprovedTesting that should make mock testing in AngularJS way easier.

我最近发布了 ngImprovedTesting,它应该使 AngularJS 中的模拟测试更容易。

To test 'myService' (from the "myApp" module) with its fooService and barService dependencies mocked out you simple can do the following in in your Jasmine test:

要使用模拟的 fooService 和 barService 依赖项测试“myService”(来自“myApp”模块),您可以在 Jasmine 测试中执行以下操作:

beforeEach(ModuleBuilder
    .forModule('myApp')
    .serviceWithMocksFor('myService', 'fooService', 'barService')
    .build());

For more information about ngImprovedTesting check out its introductory blog post: http://blog.jdriven.com/2014/07/ng-improved-testing-mock-testing-for-angularjs-made-easy/

有关 ngImprovedTesting 的更多信息,请查看其介绍性博客文章:http://blog.jdriven.com/2014/07/ng-improved-testing-mock-testing-for-angularjs-made-easy/

回答by Gal Morad

I know this is old question but there is another easier way ,you can create mock and disable the original injected at one function , it can be done by using spyOn on all the methods. see code below.

我知道这是个老问题,但还有另一种更简单的方法,您可以创建模拟并禁用在一个函数中注入的原始函数,可以通过在所有方法上使用 spyOn 来完成。见下面的代码。

var mockInjectedProvider;

    beforeEach(function () {
        module('myModule');
    });

    beforeEach(inject(function (_injected_) { 
      mockInjectedProvider  = mock(_injected_);
    });

    beforeEach(inject(function (_base_) {
        baseProvider = _base_;
    }));

    it("injectedProvider should be mocked", function () {
    mockInjectedProvider.myFunc.andReturn('testvalue');    
    var resultFromMockedProvider = baseProvider.executeMyFuncFromInjected();
        expect(resultFromMockedProvider).toEqual('testvalue');
    }); 

    //mock all service methods
    function mock(angularServiceToMock) {

     for (var i = 0; i < Object.getOwnPropertyNames(angularServiceToMock).length; i++) {
      spyOn(angularServiceToMock,Object.getOwnPropertyNames(angularServiceToMock)[i]);
     }
                return angularServiceToMock;
    }