Reputation: 4671
I have a directive that initializes the Date object several times in several functions. When Unit testing the individual functions I can handle stubbing the date like this:
(function (global) {
var NativeDate = global.Date;
global.stubDateConstructor = function (fakeDate) {
global.Date = function () {
global.Date = NativeDate;
return fakeDate;
}
}
}(this));
// ageInYears()
it("should return the age in years of the person given his/her birthdate", function() {
stubDateConstructor(new Date('2010/01/01'));
expect(ageInYears('01-01-1990')).toBe(20);
stubDateConstructor(new Date('2010/01/01'));
expect(ageInYears('01-01-1900')).toBe(110);
});
For unittesting the directive itself, which calls the ageInYears and several other similar functions this isn't going to work as I after one call to Date() stubDateConstructor will have reset Date() to the real Date object.
Is there a native way in AngularJS / Jasmine to handle these situations, or should I look into Sinon e.g.?
Upvotes: 37
Views: 26761
Reputation: 235
angular.mock.TzDate would a better native alternative here. this comes as a helper from angular mocks and truly safe guard your test from the system timezone or any other dependencies
https://docs.angularjs.org/api/ngMock/type/angular.mock.TzDate
this plays well with Jasmine or mocha
Upvotes: 1
Reputation: 38418
Jasmine (2.2) Clock can mock dates and time.
http://jasmine.github.io/2.2/introduction.html#section-Mocking_the_Date
For example (from the docs):
it("mocks the Date object and sets it to a given time", function() {
var baseTime = new Date(2013, 9, 23);
jasmine.clock().mockDate(baseTime);
jasmine.clock().tick(50);
expect(new Date().getTime()).toEqual(baseTime.getTime() + 50);
});
Upvotes: 67
Reputation: 1648
I was able to mock using a combination of sinon's fake timers to mock the window's timers and angular's mock interval service for angular to recognize time changes . Here, the countDownService under test makes use internally of both javscript Date
and angular's normal interval service Something like:
describe('when start time was 3000 milliseconds and 1001 milliseconds have passed', function() {
var startTime;
var elapse;
beforeEach(function(){
this.clock = sinon.useFakeTimers();
startTime = 3000;
elapse = 1001;
});
var elapseMillis = function(intervalMock,sinonClock,millis){
sinonClock.tick(millis);
intervalMock.flush(millis);
};
it('elapsedMillis + timeRemainingMillis should == startime',
inject(function($rootScope,$interval,countdownService) {
countdownService.startTimer(startTime);
elapseMillis($interval,this.clock,elapse);
//jasmine clock does not mock Date
//see https://github.com/pivotal/jasmine/issues/361
var elapsedMillis = countdownService.getElapsedMillis();
var timeRemainingMillis = countdownService.getTimeRemainingMillis();
expect(elapsedMillis + timeRemainingMillis).toEqual(startTime);
expect(elapsedMillis).toEqual(elapse);
}));
afterEach(function(){
this.clock.restore();
startTime = 0;
elapse = 0;
});
});
You'll want to make sure and include sinon js's sinon-timers-1.8.1.js
in your karma.conf.js files property, too.
Upvotes: 0
Reputation: 6104
A straightforward solution would be to create an Angular Dates
service that provides Date
objects for you - it might even just have a single method - Dates.now()
- that just sends back the current date by returning new Date()
. You then use this service whenever something needs to get the current date.
This then allows you to inject a different Dates
service when unit testing, for example one that always returns a specific date of your choice when called, rather than the current time.
Upvotes: 12