Модульное тестирование фабрик AngularJS, у которых есть зависимости

При модульном тестировании фабрики Angular (с Karma + Jasmine) как мне внедрить зависимость-заглушку в тестируемую фабрику?

Вот моя фабрика:

mod = angular.module('myFactoryMod', []);

mod.factory('myFactory', [
  '$log', 'oneOfMyOtherServices', function($log, svc) {
    return makeSomethingThatDoesSomethingWithTheseDependencies($log, svc);
  }
]);

oneOfMyOtherServices требуется при создании экземпляра моей фабрики.

Вот мой тест:

it('can get an instance of my factory', function() {
  var oneOfMyOtherServicesStub;

  angular.mock.module('myFactoryMod');

  oneOfMyOtherServicesStub = {
    someVariable: 1
  };

  //****How do I get my stub in my target? ****

  angular.mock.inject(['myFactory', function(target) {

      expect(target).toBeDefined();

    }
  ]);
})

N.B. Я знаю, что $controller позволяет это для контроллеров, но я не вижу эквивалента для заводов.


person Roy Truelove    schedule 15.05.2013    source источник


Ответы (2)


Я знаю два способа сделать что-то подобное:

  1. Используйте $provide и анонимный модуль, чтобы внедрить макет.
  2. Внедрите службу, которую вы хотели бы высмеять, и используйте шпионские способности жасмина для предоставления фиктивных значений.

Второй вариант работает только в том случае, если вы точно знаете, какие методы ваш тестируемый код будет вызывать внедренную службу, и вы можете легко смоделировать их. Поскольку вам кажется, что вы обращаетесь к свойству данных в службе (а не к методу), лучше всего выбрать первый вариант.

Использование $provide будет выглядеть примерно так:

describe('myFactory', function () {
  // Load your module.
  beforeEach(module('myFactoryMod'));

  // Setup the mock service in an anonymous module.
  beforeEach(module(function ($provide) {
    $provide.value('oneOfMyOtherServicesStub', {
        someVariable: 1
    });
  }));

  it('can get an instance of my factory', inject(function(myFactory) {
    expect(myFactory).toBeDefined();
  }));
});
person Noah Freitas    schedule 15.05.2013
comment
Да, я думаю, что выбор 1 - это правильный путь. Спасибо! - person Roy Truelove; 17.05.2013
comment
Я хотел бы внедрить myFactory во все тесты. Можно ли это сделать в beforeEach? Я пробовал, но не вышло ... - person Bennett McElwee; 20.09.2013
comment
Спасибо за этот ответ +1. Было бы неплохо добавить пример кода также для варианта №2, для полноты картины. - person klode; 10.12.2013
comment
@BennettMcElwee Да, можно. Примерно так: var theFactory; beforeEach(inject(function(myFactory) { theFactory = myFactory; } ))); Затем используйте theFactory в своем тесте. - person bentsai; 25.09.2014
comment
@BennettMcElwee Вы также можете пойти по соглашению и использовать символы подчеркивания, чтобы уменьшить количество переменных var theFactory; beforeEach (inject (function (myFactory) {myFactory = myFactory;}))); - person Stephane; 13.04.2015
comment
Поправьте меня, если я ошибаюсь, я понимаю, что оба варианта вводят фактическую службу, а вариант 1 заглушает один из ее методов? Разве мы не можем внедрить полную фиктивную службу, определенную в другом файле? - person Stephane; 13.04.2015
comment
Что делать, если мне нужна комбинация между издевательством над одной службой и внедрением реальной? - person Ricbermo; 12.11.2015
comment
Как я могу шпионить за методом этого oneOfMyOtherServicesStub? - person chovy; 12.04.2016
comment
Как я могу протестировать этот заводской метод getItemList, используя Jasmine и karma. Я получаю сообщение об ошибке: [$ injector: unpr] errors.angularjs (function () {angular.module ('riskCanvasApp' ) .factory ('itemsService', itemsService); itemsService. $ inject = ['$ http', '$ q', '$ compile', 'UrlService', 'accountDetailsMainService', 'sharedService', 'authenticationSvc']; функция itemsService ($ http, $ q, $ compile, urlService, accountDetailsMainService, sharedService, authenticationSvc) { - person AMRESH PANDEY; 17.04.2017

Комментарий @bentsai на самом деле очень полезен для тестирования сервисов; для полноты картины добавляю пример.

Вот тест для jasmine, который делает примерно то, что вы ищете. Примечание: для этого требуется, чтобы вы включили angular-mocks (это то, что предоставляет такие функции, как module и inject).

describe('app: myApp', function() {
  beforeEach(module('myApp'));
  var $controller;
  beforeEach(inject(function(_$controller_) {
    $controller = _$controller_;
  }));
  // Factory of interest is called MyFactory
  describe('factory: MyFactory', function() {
    var factory = null;
    beforeEach(inject(function(MyFactory) {
      factory = MyFactory;
    }))
    it('Should define methods', function() {
      expect(factory.beAwesome).toBeDefined()
      expect(factory.beAwesome).toEqual(jasmine.any(Function))
    });
  });
});

Это заглушка для того, как могли бы выглядеть модуль и связанное с ним определение фабрики:

var app = angular.module('myApp', []);
app.factory('MyFactory', function() {
  var factory = {};
  factory.beAwesome = function() {
    return 'Awesome!';
  }
  return factory;
});

В этом случае ясно, что использование inject() позволяет вам извлекать зависимости, как и следовало ожидать в обычном приложении angular, и поэтому вы можете создавать требования для поддержки тестирования вещей, которые на них полагаются.

person AJ.    schedule 26.11.2014
comment
Вы пропустили ту часть, в которой при тестировании вводили зависимость от MyFactory. - person skolsuper; 28.01.2016
comment
где вторая зависимость? - person Choco; 15.09.2016