115

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

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:

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

I can test the DI is working as such:

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.

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.

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

Thanks

David

4

7 に答える 7

184

を使用して、サービスにモックを挿入できます$provide

getSomething というメソッドを持つ依存関係を持つ次のサービスがあるとします。

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

次のように、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');
  }));

});

あなたへの呼び出しのため、$provide.value実際には明示的に myDependency を注入する必要はないことに注意してください。これは、myService の注入中に内部で発生します。ここで mockDependency を設定すると、簡単にスパイになる可能性があります。

その素晴らしいビデオへのリンクを提供してくれたloyalBrownに感謝します。

于 2013-09-12T05:53:41.407 に答える
4

私の見方では、サービス自体をモックする必要はありません。サービスの関数を単純にモックします。そうすれば、アプリ全体で行うように、実際のサービスに angular を注入できます。次に、Jasmine のspyOn関数を使用して、必要に応じてサービスの関数をモックします。

サービス自体が関数であり、一緒に使用できるオブジェクトではない場合、spyOn別の方法があります。私はこれを行う必要があり、私にとってかなりうまくいくものを見つけました。関数である Angular サービスをどのようにモックしますか? を参照してください。

于 2013-02-08T20:32:43.267 に答える
2

Angular と Jasmine での依存関係のモックを容易にする別のオプションは、QuickMock を使用することです。これは GitHub で見つけることができ、再利用可能な方法で単純なモックを作成できます。以下のリンクから GitHub から複製できます。README はかなり自明ですが、うまくいけば、将来他の人に役立つかもしれません.

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
        });
    });
    ....

上記のボイラープレートはすべて自動的に管理されるため、すべてのテストでそのモック インジェクション コードをすべて書き出す必要はありません。それが役立つことを願っています。

于 2015-01-11T04:41:49.007 に答える
2

ジョン・ガランボスの答えに加えて、サービスの特定のメソッドをモックしたいだけなら、次のようにすることができます:

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');
  }));

});
于 2015-10-19T12:26:24.593 に答える
1

コントローラーが次のような依存関係を受け入れるように作成されている場合:

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

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();
    }));
});
于 2014-06-09T19:04:47.417 に答える
1

私は最近、AngularJS での模擬テストをより簡単にする ngImprovedTesting をリリースしました。

「myService」(「myApp」モジュールから) をその fooService と barService の依存関係をモック化してテストするには、Jasmine テストで次のように簡単に実行できます。

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

ngImprovedTesting の詳細については、その紹介ブログ投稿をチェックしてください: http://blog.jdriven.com/2014/07/ng-improved-testing-mock-testing-for-angularjs-made-easy/

于 2014-07-29T00:42:18.330 に答える
0

これは古い質問であることは知っていますが、別の簡単な方法があります。モックを作成し、1 つの関数で注入されたオリジナルを無効にできます。すべてのメソッドで 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;
    }
于 2015-06-30T08:15:45.217 に答える