Stubbing Time.Now() in golang
Sometimes it’s really hard to test functionality that involve with system time. Especially when we want to test the function with a specific time. For example testing whether today is end of month or test 2 different behavior at a different time
Below we look into different ways we can mock or stub the time. Each with it’s own advantages and disadvantages.
Passing the time instance
|
|
This way you can either pass time.Now()
in your main pacakge
or call it with something like time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, time.UTC)
in your test.
Advantages:
- Simple, no extra generator necessary
- Easy to test, no need to wire mock function or struct
Disadvantages:
- Caller need to pass instance of everywhere
Passing a function that generate time
|
|
Advantages:
- This method allows you to have more control over how and when the time is generated.
For example the time is generated inside the
CheckEndOfMonth
not at the caller. - Allows the mock function to use closure inside the function
Disadvantages:
- Caller needs to pass function
Abstract the time generator as an interface
|
|
on the test code
|
|
Advantages:
- Control the generator method
- Interface can be defined small with only needed functions
- Easily switch to different implementation
Disadvantages:
- Caller need to pass instance that implements the interface
Package level time generator
Previous examples require you to pass in either concrete instance or a function on the caller.
Another approach you can use is to create a package level function to generate current time. You can change the implementation during test.
|
|
now in your test you could do something like this:
|
|
Advantages:
- No need to pass instance of time or generator
Disadvantages:
- Caller need to pass instance
- Global variable
- Not thread safe
- Need to remember to call
resetClockImplementation
Embed time generator in struct
If you are lucky enough to work in a struct, you can do this
|
|
And the test can be
|
|
I like this method because you don’t have to pass instance of time or function all over the place but still have it easily create an arbitrary time mock/stub
Make this pattern reusable
If you do this more often, you can also easily reuse the functionality into other struct
by creating embeddable
|
|
in test
|
|
Advantages:
- No need to pass instance of time or generator
- Fall back to default standard library time implementation
- Easily extend
TimeMock
by adding more function that is needed
Disadvantages:
- The caller needs to be a struct
Better way to write test function with time
Function with time is usually hard to test because it dependency with on time package.
A different way to test is to separate the function that generate the time with the function that process the time value. For example
|
|
Instead of testing that function, extract the logic of processing time
|
|
this way you don’t test the CheckEndOfMonth()
but processEndOfMonth
. This way you can easily
mock time without the need to do some wiring.