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