Инъекционных макет в AngularJS-сервис


у меня есть служба AngularJS, и я хотел бы ее проверить.

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

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

    return this;
});

Мои приложения.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 framework, однако затем я хочу провести модульное тестирование сервиса, что означает высмеивание введенных объектов.

как мне это сделать?

Я пробовал положить мои макетные объекты в модуль, например,

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 как все.

кто-нибудь знает, как я могу издеваться над введенными службами для моих модульных тестов?

спасибо

Дэвид

7 112

7 ответов:

вы можете вводить насмешки в свой сервис с помощью $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 по ссылке это отличное видео.

то, как я смотрю на это, нет необходимости издеваться над самими службами. Просто издевайтесь над функциями на сервисе. Таким образом, вы можете иметь угловые инъекции ваших реальных услуг, как это происходит во всем приложении. Затем, издеваться над функциями на службе по мере необходимости, используя Жасмин .

теперь, если сам сервис является функцией, а не объектом, который вы можете использовать spyOn С, есть другой способ пойти об этом. Мне нужно было это сделать, и я нашел то, что работает довольно хорошо для меня. Смотрите как вы издеваетесь над угловым сервисом, который является функцией?

еще один вариант, чтобы помочь сделать насмешливые зависимости проще в 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
        });
    });
    ....

он автоматически управляет всем шаблоном, упомянутым выше, поэтому вам не нужно выписывать весь этот макет кода инъекции в каждом тесте. Надеюсь, это поможет.

кроме Джон 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 в тесте жасмина, как это:

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

недавно я выпустил ngImprovedTesting, который должен сделать макет тестирования в AngularJS проще.

чтобы проверить " 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/

Я знаю, это старый вопрос, но есть еще один более простой способ ,вы можете создать макет и отключить исходный вводят в одну функцию , это можно сделать с помощью 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;
    }