Always be programming

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

Kim Persson

Written by Kim Persson (kimpers) a senior full-stack engineer with a great passion for programming, decentralization and blockchain.