time.Now Is The Winter Of Our Discontent

Part of https://discourse.jujucharms.com/t/read-before-contributing/47, an opinionated guide by William Reade

time.Now Is The Winter Of Our Discontent

You remember the thing about global variables? They’re really just an
example of mutable global state, which time.Now and time.After and
so on hook into. And if you depend on this particular mutable state, you
will write poor tests, because they will be inherently and inescapably
timing-dependent, and forever trying to balance reliability with speed.
That’s a terrible situation to be in: tests absolutely require both
properties, and it’s a strategic error to place them in opposition to
one another.

So: always, always, always configure your code with an explicit
clock.Clock. You can supply clock.WallClock in production (it’s one
of those globals that we are slowly but surely migrating towards the
edges…), but your tests must use a *testing.Clock; and use its
Alarms() method to synchronise with the SUT (you’ll get one event on
that channel for every Now(), NewTimer(), and Timer.Reset() backed
by the *testing.Clock; you can use this to ensure you only Advance()
the clock when you know it’s waiting).

(You will find loads of old code that is timing-dependent, and loads
of bad tests that do terribad things like patch a global delay var to,
say, 10ms, and set off a chain of events and wait, uhh, 50ms, and
verify that, well, between 3 and 7 things triggered in that time so on
the balance of probability it’s probably OK to- NO it is not OK!)

But with a proper testing clock, you can set up and test sensible
scenarios like, say, a 24h update-check schedule; and you can verify
that waiting for 23:59:59.999999999 does not trigger, and waiting for
24h does. Checking boundary conditions is always a good idea.