AngularJS: Как мне заглушить функцию внутри контроллера, который я тестирую?

У меня есть контроллер, который в настоящее время вызывает RESTful API при загрузке соответствующего представления. Поэтому я не уверен, связана ли эта проблема с тем, как я тестирую, или с реализацией самого кода - я был бы рад любым предложениям, которые будут решать проблему целостным образом, то есть помочь мне тестовый код, который делает то, что я хочу:)

Вот несколько примеров, иллюстрирующих мою точку зрения:

Контроллер:

angular.module('app.myModule',[])
.controller('MyController', function($scope) {

  $scope.notTestingThis = function() {
    var thisFails = $scope.do.not.want.to.mock.these.objects.substr(0,10);
  };

  $scope.testingThis = function(myString) {
    $scope.newString = myString;
  };

  $scope.notTestingThis();

});

Тест (как есть):

describe('MyControllerTest', function() {

  beforeEach(module('app.myModule'));

  var $controller;

  beforeEach(inject(function(_$controller_) {
    $controller = _$controller_;
  }));

  describe('$scope.testingThis()', function() {

    it('sets newString', function() {
      $scope = {
        'notTestingThis': {}
      };
      var myController = $controller('MyController', {$scope: $scope});
    });

  });

});

При загрузке этого представления мне нужно как-то вызвать функцию notTestingThis, однако в моем модульном тесте я хочу изолировать функцию testingThis. Проблема в том, что когда я инициализирую контроллер в моем тесте, он, конечно, вызовет notTestingThis и попытается выполнить действия с несуществующими объектами (и которые мне не нужны в этом тесте).

Очевидно, что попытка заглушить рассматриваемую функцию в соответствии с этим примером бесполезна, так как $ scope будет переписан при инициализации. Есть ли способ заглушить или смоделировать отдельные функции в контроллере, который вы пытаетесь проверить, или я где-то упустил суть? Некоторые предложения, которые выдвинул коллега:

Улучшите контроллер, чтобы он знал о самом модульном тесте, позволяя вам регулировать поток программ на основе введенного макета, то есть что-то вроде следующего в контроллере:

if (!$scope.methodA) {
   $scope.methodA = function() {...}
}

... или же...

Измените способ вызова функции notTestingThis, прослушивая событие инициализации из $ rootScope, а не вызывая его напрямую, что позволило бы мне посмеяться над $ rootScope, чтобы он не вызывал это событие, предотвращая тем самым вызов notTestingThis

Я не могу не чувствовать, что думаю об этом неправильно. Есть идеи?

1 ответ

Решение

Просто удвоить ответ на этот вопрос на случай, если другие придут к нему. Я просмотрел обе рекомендации, реализовав обе с некоторыми отличиями от моих первоначальных комментариев (поскольку я неправильно понял рекомендации), а именно:

Опция 1

Вместо того, чтобы переносить каждую функцию, оберните вызывающий код в:

if(!testingBoolean) { $scope.notTestingThis(); };

С параметром testingBoolean false, если он не передан в качестве (необязательного) аргумента контроллеру, что позволяет коду тестирования предотвращать вызовы

Вариант 2:

$ широковещательная рассылка от $rootScope с использованием сервисной функции. Оберните вызывающий код в слушателе этого события, а затем вызовите функцию из контроллера. Насмешка над $rootScope при выполнении тестов предотвратит запуск функции notTestingThis.

Реальное решение

Эти подходы, похоже, отвлекают; хотя это нигде явно не указано, похоже, что контроллеры не должны сами вызывать функции. Правильный путь здесь заключается в том, чтобы изменить архитектуру приложения так, чтобы директивы вызывали функции через привязки, упрощая тем самым модульное тестирование.

Другие вопросы по тегам