Внедрение макета в службу AngularJS

У меня есть служба AngularJS, и я хотел бы 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;
});

В моем файле 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, но затем я хочу unit test службу, что означает издевательство над вложенными объектами.

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

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

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

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

Спасибо

Дэвид

105
08 февр. '13 в 16:06
источник поделиться
7 ответов

Вы можете вводить mocks в свою службу, используя $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 за ссылку отличное видео.

168
12 сент. '13 в 8:53
источник

Связанные вопросы


Похожие вопросы

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

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

4
08 февр. '13 в 23:32
источник

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

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

2
11 янв. '15 в 7:41
источник

В дополнение к ответ Джона Галамбоса: если вы просто хотите издеваться над определенными методами службы, вы можете сделать это следующим образом:

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

});
2
19 окт. '15 в 15:26
источник

Если ваш контроллер написан так, чтобы принимать зависимость, например:

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();
    }));
});
1
09 июня '14 в 22:04
источник

Недавно я выпустил 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/

1
29 июля '14 в 3:42
источник

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

Посмотрите другие вопросы по меткам или Задайте вопрос