Time is an illusion. Lunchtime doubly so.
Testing things that depend on the current time is annoying.
Testing things that do things after certain time intervals is incredibly annoying.
Java 8’s java.time
package encourages you to inject a Clock
instance into things that need to generate timestamps.
All well and good but when you want to write a test like…
Given some initial state
Then things behave thus
When x amount of time has passed
Then things now behave thus
…you pretty soon discover that all the Clock
implementations provided by Java are immutable.
Sure, you can do that but…
If you stub a sequence of instants the clock will return, your test becomes tightly coupled to the exact number of times your code asks for the current time.
Insert an innocent new clock.instant()
call and all your tests break.
Also java.time.Clock
is an abstract class (even though there’s nothing about it that would appear to prevent it just being a Java 8 interface with default methods) and some mocking frameworks don’t play nice with classes.
Create a seam in your class-under-test where you can inject a Clock
.
When running the application for real you’d inject Clock.systemDefaultZone()
or Clock.systemUTC()
or whatever.
In tests you instead inject a MutableClock
.
A MutableClock
is pretty simple.
You can create one, optionally giving it an Instant
and/or ZoneId
(Instant.now()
and ZoneId.systemDefault()
are the defaults if you don’t specify):
link:test-clock-core/src/test/java/io/github/robfletcher/time/MutableClockTests.java[role=include]
You can then:
…Convert the mutable clock to a fixed one (as per Clock.fixed
) with toFixed()
:
link:test-clock-core/src/test/java/io/github/robfletcher/time/MutableClockTests.java[role=include]
…Advance the time represented by the clock with advanceBy(TemporalAmount)
:
link:test-clock-core/src/test/java/io/github/robfletcher/time/MutableClockTests.java[role=include]
…Assign a completely different instant to the clock with instant(Instant)
:
link:test-clock-core/src/test/java/io/github/robfletcher/time/MutableClockTests.java[role=include]
I was writing a queue that:
-
allows clients to specify "deliver after" time.
-
re-queues messages that are not acknowledged within a time limit.
So I needed to write tests like:
Given a message with a delivery time is pushed to the queue
When I ask the queue for messages
Then I get nothing back
Given a message with a delivery time is pushed to the queue
And enough time elapses
When I ask the queue for messages
Then I get my message back
Given a message is pushed to the queue
And I read and acknowledge the message
And the acknowledge time limit elapses
When I ask the queue for messages
Then I get nothing back
Given a message is pushed to the queue
And I read the message but never acknowledge it
And the acknowledge time limit elapses
When I ask the queue for messages
Then I get the same message back again