Choosing dates and times in tests
A little note to avoid bugs in tests, or missing bugs: don’t use the current date or time when writing unit tests.
When making up data for tests, it’s common to just use today’s date as it’s easy to think up. This can catch you out when there is actually some hidden dependency on the real time and your test starts failing the next day, month or year. The test is currently passing by coincidence, which is hard to distinguish from a real pass if you’re not careful.
It’s better to use a specific set of dates and times that cover some common pitfalls such as daylight savings, leap years, year and month transitions and so on. It’s also wise to make sure that both past and future dates are covered in the tests.
On a related note, it’s always a good idea to pass the current time into a class
or method, rather than allowing it to grab it directly. It should be common to
see a parameter called now
. It’s fine if this defaults to the current system
time, like this:
function fooMyBar(now: moment.Moment = moment.utc()) {
// whatever
}
This function is now far more testable, and you can easily set up useful test cases without any awkwardness or special libraries:
// Given it is February 29th;
const now = moment.utc('2020-02-29');
// When we foo the bar;
fooMyBar(now);
// Then something in particular should happen;
// ...
Coming back to avoiding the current date in test data, the principle applies to test data more generally – avoid test data that coincidentally happens to be true. Well, test those cases, but make sure to also test the cases which distinctly differ from any expected reality.
This is another reason to try and keep the habit of getting a failing test case first, to make sure your test isn’t passing by coincidence.