У меня есть служба, которая имеет следующий метод (среди прочих), который возвращает обещание $ 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, я получаю неопределенное значение в качестве результата.
Что я делаю не так?
Я вижу больше проблем в вашем примере.
Основная проблема - определение успеха в макете. Успех - это функция, которая имеет функцию в качестве параметра - обратного вызова. Обратный вызов вызывается при получении данных - данные передаются в качестве первого аргумента.
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();
});