How to test a function that works with time

Published on

While building Minimal Analytics I had the need to test a function that was dependent on time.

In my case, the function returned the milliseconds until midnight.

Here is a simple way to test the function msUntilMidnight.

The test case

The simplest test I could come up with, looks as follows:

const test = require('ava')
const { msUntilMidnight } = require('./backup')
const HOURS = 60 * 60 * 1000

test('returns ms until midnight', t => {
  const relativeNow = +new Date(new Date().toISOString().substring(0, 11) + '22:00:00.000Z')

  t.is(msUntilMidnight(relativeNow), 2 * HOURS)
})

You might notice the “weird” variable relativeNow.

It resembles a “static” date set to 10PM of the current day.

The assertion I make is that 2 hours (in milliseconds) are remaining until midnight.

This “trick” is needed since if you didn’t have this static date, you would always get a different assertion error.

How does this work in relation to the actual code though?

The application code

The idea is the following:

  • in the test case, I simulate a “static” date at 10PM of the current date
  • in the actual application code, I leverage the default value Date.now()

msUntilMidnight looks like this:

function msUntilMidnight (now = Date.now()) {
  let midnight = new Date()
  midnight.setDate(midnight.getDate() + 1)
  midnight = new Date(midnight.toISOString().substring(0, 11) + '00:00:00.000Z')
  return +midnight - now
}

Use case

The use case I found for Minimal Analytics is for backups.

I wanted to make the stupidest backup solution, that runs every midnight.

The backup module looks like this:

const fs = require('fs')

module.exports = {
  start,
  backup,
  msUntilMidnight
}

function start (options = {}) {
  setTimeout(backup, msUntilMidnight(), options)
}

function backup (options = {}, scheduleNext = true) {
  console.log(new Date().toISOString(), 'backing up', options.DATA_PATH)
  const backupPath = options.DATA_PATH + '.bkp'
  fs.copyFileSync(options.DATA_PATH, backupPath)
  scheduleNext && setTimeout(backup, msUntilMidnight(), options)
}

function msUntilMidnight (now = Date.now()) {
  let midnight = new Date()
  midnight.setDate(midnight.getDate() + 1)
  midnight = new Date(midnight.toISOString().substring(0, 11) + '00:00:00.000Z')
  return +midnight - now
}

And you would start the backup service with a simple backup.start().

Open source example

You can find the code for Minimal Analytics on Github

Here is also the direct link to the backup module and the related test

Here, have a slice of pizza 🍕