Mocking time with Go
Every now and then I find developers struggling with handling time
in their tests, so I wanted to address that in this blog post. Typically there would be code that looks something like the below that needs to be tested:
import (
"fmt"
"time"
)func WhatTimeIsIt() string {
return fmt.Sprintf("It's %d", time.Now().Unix())
}
However, since the time keeps changing this will be very difficult to capture in a test. Because you will sometimes run the test when the code execution and the test variable assignment happens on two different seconds.
This is where libraries like jonboulle/clockwork
comes to the rescue. They allow you to mock time,so that you always get predictable results.
You’d need to refactor your code to look like this:
import (
"fmt" "github.com/jonboulle/clockwork"
)func WhatTimeIsIt(clock clockwork.Clock) string {
return fmt.Sprintf("It's %d", clock.Now().Unix())
}
Let’s address the “OMG, how ugly!” comment soon. But what I’m doing here is pretty straight forward. The time
needs to be mocked. And we know that for mocking an interface
is helpful. And that’s exactly what jonboulle/clockwork
provides us (along with some neat helpers).
Now we can write a test like this:
import (
"fmt"
"testing" "github.com/jonboulle/clockwork"
"github.com/stretchr/testify/assert"
)func TestWhatTimeIsIt(t *testing.T) {
tests := map[string]struct {
clock clockwork.Clock
}{
"basic test": {
clock: clockwork.NewFakeClock(),
},
} for name, test := range tests {
t.Logf("Running test case: %s", name) output := WhatTimeIsIt(test.clock)
assert.Equal(
t,
fmt.Sprintf("It's %d", test.clock.Now().Unix()),
output,
)
}
}
Alrighty. The test is quite straight forward. What’s worth highlighting though is:
- We’re passing a fake implementation of
time
toWhatTimeIsIt()
. - In production we’d obviously need to send a real clock. Like this for example
WhatTimeIsIt(clockwork.NewRealClock())
.