AngularJS: тестирование фабрики, которая возвращает обещание, в то же время высмеивая сервис, который использует $ http

0

У меня есть служба, которая имеет следующий метод (среди прочих), который возвращает обещание $ http

  function sessionService($http, serviceRoot) {
    return {
        getAvailableDates: function () {
            return $http.get(serviceRoot + '/session/available_dates');
        }
    };
  };

  angular.module('app').service('sessionService', ['$http', 'serviceRoot', sessionService]);

И затем другая фабрика, которая ее обертывает и кэширует/добавляет данные в localStorage. Это возвращает регулярное обещание

angular.module('app')
    .factory('AvailableDates', AvailableDates);

AvailableDates.$inject = ['sessionService', '$window', '$q'];

function AvailableDates(sessionService, $window, $q) {
    var availableDates = [];

    return {
        getAvailableDates: getAvailableDates
    };

    function getAvailableDates() {
        var deferred = $q.defer();
        var fromStorage = JSON.parse($window.sessionStorage.getItem('validDates'));

        if (availableDates.length > 0) {
            deferred.resolve(availableDates);
        } else if (fromStorage !== null) {
            deferred.resolve(fromStorage);
        } else {
            sessionService.getAvailableDates()
                .success(function (result) {
                    availableDates = result;
                    $window.sessionStorage.setItem('validDates', JSON.stringify(availableDates));
                    deferred.resolve(availableDates);
                });
        }
        return deferred.promise;
    }
}

Все это прекрасно работает. Моя проблема заключается в том, что я не могу понять, как тестировать эту вещь, одновременно высмеивая sessionService. Я прочитал все связанные с ним вопросы о стеке и попробовал всевозможные разные вещи, но безрезультатно.

Вот как выглядит мой тест:

describe('testing AvailableDates factory', function () {
    var mock, service, rootScope, spy, window, sessionStorageSpy, $q;
    var dates = [ "2014-09-27", "2014-09-20", "2014-09-13", "2014-09-06", "2014-08-30" ];
    var result;

    beforeEach(module('app'));

    beforeEach(function() {
        return angular.mock.inject(function (_sessionService_, _AvailableDates_, _$rootScope_, _$window_, _$q_) {
            mock = _sessionService_;
            service = _AvailableDates_;
            rootScope = _$rootScope_;
            window = _$window_;
            $q = _$q_;
        });
    });

    beforeEach(inject(function () {
        // my service under test calls this service method
        spy = spyOn(mock, 'getAvailableDates').and.callFake(function () {
            return {
                success: function () {
                    return [ "2014-09-27", "2014-09-20", "2014-09-13", "2014-09-06", "2014-08-30" ];
                },
                error: function() {
                    return "error";
                }
            };
        });

        spyOn(window.sessionStorage, "getItem").and.callThrough();
    }));

    beforeEach(function() {
        service.getAvailableDates().then(function(data) {
            result = data;
            // use done() here??
        });
    });

    it('first call to fetch available dates hits sessionService and returns dates from the service', function () {
        rootScope.$apply(); // ??

        console.log(result); // this is printing undefined

        expect(spy).toHaveBeenCalled();  // this passes
        expect(window.sessionStorage.getItem).toHaveBeenCalled(); // this passes
    });
});

Я пробовал разные вещи, но не могу понять, как проверить результат вызова AvailableDates.getAvailableDates(). Когда я использую done(), я получаю ошибку: Timeout - обратный вызов Async не вызывается с таймаутом, указанным jasmine.DEFAULT_TIMEOUT_INTERVAL (я пробовал переопределить это значение, не повезло).

Если я вытащил done() и просто вызвал rootScope. $ Apply() после вызова.then, я получаю неопределенное значение в качестве результата.

Что я делаю не так?

Теги:
unit-testing
jasmine
angular-promise

1 ответ

2
Лучший ответ

Я вижу больше проблем в вашем примере.

Основная проблема - определение успеха в макете. Успех - это функция, которая имеет функцию в качестве параметра - обратного вызова. Обратный вызов вызывается при получении данных - данные передаются в качестве первого аргумента.

return {
    success: function (callback) {
        callback(dates);
    }
};

Упрощенный рабочий пример приведен здесь http://plnkr.co/edit/Tj2TZDWPkzjyhsuSM0u3?p=preview

В этом примере mock передается провайдеру с помощью функции модуля (из ngMock) - вы можете передать объект с ключом (имя службы) и значением (реализация). Эта реализация будет использоваться для инъекций.

module({
      sessionService:sessionServiceMock
});

Я думаю, что тестовая логика должна быть в одной функции (тест), разбивать ее на beforeEach и тест не является хорошим решением. Тест - мой пример; это более читаемо и четко разделенные части - организуют, действуют, утверждают.

inject(function (AvailableDates) {
    AvailableDates.getAvailableDates().then(function(data) {
      expect(data).toEqual(dates);
      done();
    });

    rootScope.$apply(); // promises are resolved/dispatched only on next $digest cycle

    expect(sessionServiceMock.getAvailableDates).toHaveBeenCalled();
    expect(window.sessionStorage.getItem).toHaveBeenCalled();
  });

Ещё вопросы

Сообщество Overcoder
Наверх
Меню