web-dev-qa-db-ja.com

AngularJSサービスの単体テスト時に依存サービスを注入する

私はサービスAをテストしていますが、サービスAはサービスBに依存しています(つまり、サービスBはサービスAに注入されます)。

この質問 を見ましたが、私の意見ではmockサービスBの方が理にかなっているので、私の場合は少し異なりますサービスBの実際のインスタンスを注入する代わりに、ジャスミンスパイでそれをモックします。

サンプルテストを次に示します。

describe("Sample Test Suite", function() {

  beforeEach(function() {

    module('moduleThatContainsServiceA');

    inject([
      'serviceA', function(service) {
        this.service = service;
      }
    ]);

  });

  it('can create an instance of the service', function() {
    expect(this.service).toBeDefined();
  });
});

私が得るエラーは次のとおりです:

エラー:不明なプロバイダー:serviceBProvider

どうすればこのようなことができますか?

51
Roy Truelove

実際、AngularJSでは、依存性注入は「最後の勝ち」ルールを使用します。したがって、モジュールと依存関係を含めた直後にテストでサービスを定義できます。テストするサービスAがDIを使用してサービスBを要求すると、AngularJSはサービスBのモックバージョンを提供します。

これは多くの場合、MyAppMocksなどの新しいモジュールを定義し、そこに模擬サービス/値を配置し、このモジュールを依存関係として追加するだけで行われます。

種類(概略):

beforeEach(function() {
  angular.module('MyAppMocks',[]).service('B', ...));
  angular.module('Test',['MyApp','MyAppMocks']);
  ...
45

私はCoffeeScriptでこれをやっていて、余分な落とし穴を見つけました。 (また、このページのコードは混乱を招くほど簡潔であることがわかりました。)完全な動作例を次に示します。

describe 'serviceA', ->
   mockServiceB = {}

   beforeEach module 'myApp' # (or just 'myApp.services')

   beforeEach ->
      angular.mock.module ($provide) ->
         $provide.value 'serviceB', mockServiceB
         null

   serviceA = null
   beforeEach inject ($injector) ->
      serviceA = $injector.get 'serviceA'

   it 'should work', ->
      expect( true ).toBe( true )
      #serviceA.doStuff()

$provide.valueの後に明示的にnullを返さずに、Error: Argument 'fn' is not a function, got Objectを取得し続けました。私はこれで答えを見つけました Googleグループスレッド

24
jab

Valentynのソリューションはうまくいきましたが、別の方法があります。

beforeEach(function () {

    angular.mock.module("moduleThatContainsServiceA", function ($provide) {
                $provide.value('B', ...);
            });
});

その後、AngularJSサービスAが依存性注入によってサービスBを要求すると、moduleThatContainsServiceAのサービスBの代わりにサービスBのモックが提供されます。

この方法では、サービスをモックするためだけに追加のangularモジュールを作成する必要はありません。

20
Richard Keller

最も単純な方法は、サービスBを注入して、それを模擬することです。例えばサービスカーはサービスエンジンに依存します。ここで、自動車をテストするときにエンジンをモックする必要があります。

describe('Testing a car', function() {
      var testEngine;

  beforeEach(module('plunker'));
  beforeEach(inject(function(engine){
    testEngine = engine;
  }));

  it('should drive slow with a slow engine', inject(function(car) {
    spyOn(testEngine, 'speed').andReturn('slow');
    expect(car.drive()).toEqual('Driving: slow');
  }));
});

参照: https://github.com/angular/angular.js/issues/1635

6
gm2008

これは私のために働いたものです。重要なのは、モックされる実際のモジュールを定義することです。 angular.mock.moduleを呼び出すと、実際のモジュールがモック可能になり、接続できるようになります。

    beforeEach( ->
        @weather_service_url = '/weather_service_url'
        @weather_provider_url = '/weather_provider_url'
        @weather_provider_image = "test.jpeg"
        @http_ret = 'http_works'
        module = angular.module('mockModule',[])
        module.value('weather_service_url', @weather_service_url)
        module.value('weather_provider_url', @weather_provider_url)
        module.value('weather_provider_image', @weather_provider_image)
        module.service('weather_bug_service', services.WeatherBugService)

        angular.mock.module('mockModule')

        inject( ($httpBackend,weather_bug_service) =>
            @$httpBackend = $httpBackend
            @$httpBackend.when('GET', @weather_service_url).respond(@http_ret)
            @subject = weather_bug_service
        )
    )
1
Nick