How to test time-dependent code in JavaScript

Published 10/30/2020

Say you have a piece of code that depends on the current time.

function isAM() {
 return new Date().getHours() < 12
}

Let's create a test for this.

it('is AM when it is before 12 noon', () => {
  return isAM()
})

The problem is that this test works fine before 12 noon, but will fail afterward.

To fix this, we can pass the date through the function as an argument.

function isAM(date = new Date()) {
 return date.getHours() < 12
}

Now we can create tests checking a variety of dates.

So far there is nothing special happening. Allowing a date to be passed makes perfect sense for a function like isAM, but let's take this a step further.

Check out my e-book!

Learn to simplify day-to-day code and the balance between over- and under-engineering.

Say, you now have another function that depends on isAM:

function isDeliverable(item) {
  if (isAM()) {
    return false
  }

  // ...

  return true
}

For whatever reason, items are not deliverable before 12 pm.

Now how do we test this method? Passing a date to isDeliveryable certainly does not make much sense.

This is where we can make use of a handy npm library called timekeeper.

npm i timekeeper --save-dev

timekeeper allows us to travel to a different time by mocking the native Date object.

Here's how we can test will look like:

const timekeeper = require('timekeeper')
const { isDeliverable } = require('./delivery')

it('is not deliverable before 12 noon', () => {
  timekeeper.travel(new Date(2020, 1, 1, 7))

  expect(isDeliverable({ ... })).toBeFalsy()

  timekeeper.reset()
})

Awesome!! It's like Rails.

As this mutates the native Date object, it's best to not run tests in parallel!