Mocking the system clock with Jest

qmenoret

Quentin Ménoret

Posted on September 7, 2020

Mocking the system clock with Jest

Mocking the system clock is extremely important when you are dealing with testing. It's important so you can deal with time-based tests -  say a test that deals with ensuring that a certain feature is only available during working hours for, instance.
Let's have a look at an even simpler use case. You want a function that tells you if a date is in the future.

function isInTheFuture(date) {
  return new Date().getTime() - date.getTime() < 0
}
Enter fullscreen mode Exit fullscreen mode

A very simple way to deal with this unit test would be to test it with a date long passed, or far away in the future. This way the test will be green (for the next 30 years at least).

it('returns true when the date is in the future', () => {
  const date = new Date('2050-01-01')
  expect(isInTheFuture(date)).toBe(true)
})
Enter fullscreen mode Exit fullscreen mode

Another way to do this is to extract the current date as an argument to your function so you can actually test it:

function isInTheFuture(currentDate, dateToTest) {
  return currentDate.getTime() - dateToTest.getTime() < 0
}

it('compares two dates', () => {
  const currentDate = new Date('2019-01-01')
  const dateInTheFuture = new Date('2020-01-01')
  expect(isInTheFuture(currentDate, dateInTheFuture)).toBe(true)
})
Enter fullscreen mode Exit fullscreen mode

This way, it is very easy to unit test, but it is not as easy to understand or maintain.

Another "common" way of doing this would be to manually monkey patch the date object. But that's error-prone, and it's better to leave that responsibility to someone else.
Fortunately, in version 26, Jest introduced a new and more powerful time mock. They enabled the usage of @sinonjs/fake-timers, even though, for now, the feature is still a bit hidden.
To use the new mock system, you need to pass the "modern" argument to the jest.useFakeTimers function. This system will allow you not only to mock timers as you already could but also to mock the system clock.

Give the first implementation, you would be able to write tests that looks like this:

describe('Time based test with mock', () => {
  beforeAll(() => {
    jest.useFakeTimers('modern')
    jest.setSystemTime(new Date('2017-01-01'))
  })
  afterAll(() => {
    jest.useRealTimers()
  })

  it('returns true when the date is in the future', () => {
    const date = new Date('2019-01-01')
    expect(isInTheFuture(date)).toBe(true)
  })
})
Enter fullscreen mode Exit fullscreen mode

This way, the test will be green, but will also be stable in time. This new mock system will become the default in Jest 27. Until then, we'll have to add that extra parameter to the useFakeTimers call.

💖 💪 🙅 🚩
qmenoret
Quentin Ménoret

Posted on September 7, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related