Модульное тестирование углового контроллера и службы, которые используют обещание?

#angularjs #unit-testing #asynchronous #jasmine #karma-jasmine

#angularjs #модульное тестирование #асинхронный #jasmine #карма-жасмин

Вопрос:

Я не могу передать результат теста, я использую очень простую реализацию, чтобы глубже понять тестирование.

У меня есть фабрика, которая возвращает обещание, доступ к которому осуществляется с моего контроллера. Я хочу проверить, что вызов выполнен успешно и присваивает ответ repos переменной. Ниже приведен код:

 'use strict';

angular.module('app')
  .factory('searchServ', function ($timeout, $q, $http) {
    return {
      fetch: function(user) {
        var deferred = $q.defer();

        $timeout(function(){
          $http({method: 'GET', url: 'https://api.github.com/users/'   user   '/repos'}).then(function(repos) {
            deferred.resolve(repos.data);
          }, function(reason){
            deferred.reject(reason.status);
            console.log(reason);
          });
        }, 30);

        return deferred.promise;
      }
    };
  })
  .controller('MainCtrl', function ($scope, searchServ) {
    $scope.results = function(user) {
      $scope.message = '';
      searchServ.fetch(user).then(function (repos) {
        if(repos.length){
          $scope.message = '';
          $scope.repos = repos;
        }
        else{
          $scope.message = 'not found'
        }
      }, function (){
        $scope.message = 'not found';
      });
    };
  });

//Test

'use strict';

describe('MainCtrl', function () {
  var scope, searchServ, controller, deferred, repos = [{name: 'test'}];
  // load the controller's module
  beforeEach(module('app'));

  beforeEach(inject(function($controller, $rootScope, $q) {
    searchServ = {
      fetch: function () {
        deferred = $q.defer();
        return deferred.promise;
      }
    };
    spyOn(searchServ, 'fetch').andCallThrough();
    scope = $rootScope.$new();
    controller = $controller('MainCtrl', {
      $scope: scope,
      fetchGithub: fetchGithub
    });


  }));
  it('should test', function () {
    expect(scope.test).toEqual('ha');
  });

  it('should bind to scope', function () {
    scope.results();
    scope.$digest();
    expect(scope.message).toEqual('');
    //expect(scope.repos).not.toBe(undefined);
  });
});
  

Запуск теста выдает следующую ошибку :

TypeError: undefined is not a function (evaluating 'spyOn(searchServ, 'fetch').andCallThrough()') in test/spec/controllers/main.js (line 15)

Есть идеи, как я могу протестировать это так, чтобы оно проверяло привязку области видимости, а также асинхронный вызов?

Комментарии:

1. Нет andCallThrough . Это так and.callThrough . Вы широко используете отложенный антипаттерн . Что в вашем случае обернется плохо. Потому что searchServ.fetch возвращает неразрешенное обещание в вашем случае.

2. Я пробовал вызов and.callThrough, но он всегда возвращает undefined, я также пробовал другой подход, например, эту скрипку jsfiddle.net/upoc0ott/2 но все равно остается неопределенным, было бы удивительно, если бы кто-нибудь мог мне помочь, используя любой из способов?

Ответ №1:

В вашем коде много проблем.

Я создал этот Plunkr для этой цели. index.js это файл с вашим кодом и тестовыми примерами. Я отредактировал большую часть части в соответствии с соглашениями и рекомендациями.

Есть несколько советов, которые я хотел бы вам дать:

  • Поскольку $http возвращает обещание, вы должны использовать это вместо разрешения обещания и создания другого обещания из вашего метода. Не уверен, почему используется тайм-аут. Поэтому я удалил $q и $timeout из searchServ зависимостей.
  • Я сделал то же самое в тестовом примере, удалив deferred переменную, которую вы использовали.
  • Вы должны использовать angular-mocks.js для моделирования своих сервисов и других зависимостей вместо определения сервиса внутри вашего тестового примера (как вы это сделали).)
  • Вы должны создать отдельные describe блоки для тестирования разных частей вашего кода ( controller в данном случае a).

Надеюсь, это поможет!

Комментарии:

1. Это был блестящий ответ! Большое спасибо, что нашли время. Я использовал $ timeout и $ q, потому что они основаны на обещаниях, и если вы создадите фабрику / сервис через yeoman, это даст вам такой подход, но поскольку $ http также возвращает обещание, я думаю, что это работает так же хорошо! 🙂