Mocking date and time in tests with Typescript & Jest
October 31, 2019
Recently I have been working on a rather complex feature that relies heavily on current dates, the day of the month affects the execution path that the program takes. To try to iron out the edge cases I wanted to implement good unit test coverage for this feature.
In order to control the execution flow I needed to be able to freeze and change the date and time returned from
new Date()
and Date.now()
but this was not as trivial as I had anticipated.
After spending some time digging through various issues on GitHub I ended up writing a custom utility function for date mocking.
This was written for a TypeScript and Jest based Node.js project but it should work with any testing framework.
Date mocking code
/**
* @param {Date} expected The date to which we want to freeze time
* @returns {Function} Call to remove Date mocking
*/
export const mockDate = (expected: Date) => {
const _Date = Date
// If any Date or number is passed to the constructor
// use that instead of our mocked date
function MockDate(mockOverride?: Date | number) {
return new _Date(mockOverride || expected)
}
MockDate.UTC = _Date.UTC
MockDate.parse = _Date.parse
MockDate.now = () => expected.getTime()
// Give our mock Date has the same prototype as Date
// Some libraries rely on this to identify Date objects
MockDate.prototype = _Date.prototype
// Our mock is not a full implementation of Date
// Types will not match but it's good enough for our tests
global.Date = MockDate as any
// Callback function to remove the Date mock
return () => {
global.Date = _Date
}
}
Mock usage
Using the mock is very simple just call mockDate
with a date object representing the point in time
you want to freeze ❄. When want to restore 🕐 you just call the callback function returned from mockDate
.
import { mockDate } from "./test/mocks"
new Date() // 2019-10-31T22:01:00.786Z
const timeFreeze = new Date("2019-10-01T00:00:01.30Z")
const resetDateMock = mockDate(timeFreeze)
// Time is ❄
new Date() // 2019-10-01T00:00:01.30Z
Date.now() === timeFreeze.getTime() // true
resetDateMock()
// 🕐 is ticking again
new Date() // 2019-10-31T22:01:00.786Z