[javascript] AngularJS 서비스에 모의 객체 삽입

AngularJS 서비스가 작성되었으며 단위 테스트를 원합니다.

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

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

    return this;
});

내 app.js 파일에는 다음이 등록되어 있습니다.

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

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

이것은 서비스가 DI 프레임 워크에 의해 생성 될 수 있음을 입증했지만, 다음에 서비스를 단위 테스트하고 싶습니다. 즉, 삽입 된 객체를 조롱하는 것을 의미합니다.

어떻게해야합니까?

모의 객체를 모듈에 넣어 보았습니다.

beforeEach(module(mockNavigationService));

서비스 정의를 다음과 같이 다시 작성합니다.

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

그러나 후자는 DI에 의해 생성되는 서비스를 모두 중단하는 것 같습니다.

아무도 내 단위 테스트를 위해 주입 된 서비스를 모의 할 수있는 방법을 알고 있습니까?

감사

데이비드



답변

를 사용하여 서비스에 모의를 삽입 할 수 있습니다 $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 에게 감사드립니다 .


답변

제가보기에는 서비스 자체를 조롱 할 필요가 없습니다. 서비스의 기능을 조롱하기 만하면됩니다. 이렇게하면 앱 전체 에서처럼 실제 서비스를 각도 주입 할 수 있습니다. 그런 다음 Jasmine의 spyOn기능을 사용하여 필요에 따라 서비스의 기능을 모의합니다 .

이제 서비스 자체가 함수이고 spyOn함께 사용할 수있는 객체가 아니라면 다른 방법이 있습니다. 이 작업을 수행해야했고 저에게 잘 맞는 것을 찾았습니다. 함수 인 Angular 서비스를 어떻게 모의합니까?를 참조하십시오 .


답변

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

위에서 언급 한 모든 상용구를 자동으로 관리하므로 모든 테스트에서 모의 ​​주입 코드를 모두 작성할 필요가 없습니다. 도움이되기를 바랍니다.


답변

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

});


답변

컨트롤러가 다음과 같은 종속성을 갖도록 작성된 경우 :

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


답변

나는 최근에 AngularJS에서 모의 ​​테스트를 더 쉽게 만들어 줄 ngImprovedTesting을 출시했습니다.

fooService 및 barService 종속성이 모의 된 ‘myService'( “myApp”모듈에서)를 테스트하려면 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/


답변

나는 이것이 오래된 질문이라는 것을 알고 있지만 또 다른 쉬운 방법이 있습니다. 모의를 만들고 하나의 기능에 주입 된 원본을 비활성화 할 수 있습니다. 모든 방법에서 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;
    }