My service includes a method that returns an $http promise:
function sessionService($http, serviceRoot) {
return {
getAvailableDates: function () {
return $http.get(serviceRoot + '/session/available_dates');
}
};
};
angular.module('app').service('sessionService', ['$http', 'serviceRoot', sessionService]);
Additionally, I have another factory that wraps it and manages data stored in localStorage. This factory returns a regular promise:
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;
}
}
Although this setup works fine, I'm struggling to test it while mocking the sessionService. Despite reading numerous resources and attempting various methods, I haven't been successful.
Below is my current testing approach:
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;
// Should I use done() here??
});
});
it('first call to fetch available dates hits sessionService and returns dates from the service', function () {
rootScope.$apply(); // Any corrections needed here??
console.log(result); // Outputting as undefined
expect(spy).toHaveBeenCalled(); // Test passes
expect(window.sessionStorage.getItem).toHaveBeenCalled(); // Test passes
});
});
I have attempted different approaches without success in testing the result of AvailableDates.getAvailableDates() call. When using done(), I encounter a timeout error - "Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL". If I exclude done() and directly call rootScope.$apply() after the .then is executed, the result remains undefined.
Any insights on what could be causing this issue?