Is there an easy way to stub out time.Now() globally during test?

Unit TestingGo

Unit Testing Problem Overview


Part of our code is time sensitive and we need to able to reserve something and then release it in 30-60 seconds etc, which we can just do a time.Sleep(60 * time.Second)

I have just implemented time interface and during test use a stubbed implementation of the time interface, similar to this golang-nuts discussion.

However, time.Now() is called in multiple sites which means we need to pass a variable around to keep track of how much time we have actually slept.

I was wondering if there is an alternative way to stub out time.Now() globally. Maybe making a system call to change the system clock?

Maybe we can write our own time package which basically wraps around the time package but allows us to change it?

Our current implementation works well, I am a go beginner and I am curious to see if anyone has other ideas?

Unit Testing Solutions


Solution 1 - Unit Testing

With implementing a custom interface you are already on the right way. I take it you use the following advise from the golang nuts thread you've posted:


type Clock interface {
  Now() time.Time
  After(d time.Duration) <-chan time.Time
}

> and provide a concrete implementation

type realClock struct{}
func (realClock) Now() time.Time { return time.Now() }
func (realClock) After(d time.Duration) <-chan time.Time { return time.After(d) }

> and a testing implementation.


Original

Changing the system time while making tests (or in general) is a bad idea. You don't know what depends on the system time while executing tests and you don't want to find out the hard way by spending days of debugging into that. Just don't do it.

There is also no way to shadow the time package globally and doing that would not do anything more you couldn't do with the interface solution. You can write your own time package which uses the standard library and provides a function to switch to a mock time library for testing if it is the time object you need to pass around with the interface solution that is bothering you.

The best way to design and test your code would probably be to make as much code stateless as possible. Split your functionality in testable, stateless parts. Testing these components separately is much easier then. Also, less side effects means that it is much easier to make the code run concurrently.

Solution 2 - Unit Testing

If the methods you need to mock are few, such as Now(), you can make a package variable which can be overwritten by tests:

package foo

import "time"

var now = time.Now

// The rest of your code...which calls now() instead of time.Now()

then in your test file:

package foo

import (
    "testing"
    "time"
)

var now = func() time.Time { return ... }

// Your tests

Solution 3 - Unit Testing

I use the bouk/monkey package to replace the time.Now() calls in my code with a fake:

package main

import (
	"fmt"
	"time"

	"github.com/bouk/monkey"
)

func main() {
	wayback := time.Date(1974, time.May, 19, 1, 2, 3, 4, time.UTC)
	patch := monkey.Patch(time.Now, func() time.Time { return wayback })
    defer patch.Unpatch()
	fmt.Printf("It is now %s\n", time.Now())
}

This works well in tests to fake out system dependencies and avoids the abused DI pattern. Production code stays separate from test code and you gain useful control of system dependencies.

Solution 4 - Unit Testing

Also if you need to just stub time.Now you can inject dependency as a function, e.g.

func moonPhase(now func() time.Time) {
  if now == nil {
    now = time.Now
  }

  // use now()...
}

// Then dependent code uses just
moonPhase(nil)

// And tests inject own version
stubNow := func() time.Time { return time.Unix(1515151515, 0) }
moonPhase(stubNow)

Granted all that is a bit ugly if you come from dynamic languages background (e.g. Ruby) :(

Solution 5 - Unit Testing

There are multiple way to mock or stub time.Now() in test code:

  • Passing an instance of time to the function
func CheckEndOfMonth(now time.Time) {
  ...
}
  • Passing a generator to the function
CheckEndOfMonth(now func() time.Time) {
	// ...
	x := now()
}
  • Abstract with an interface
type Clock interface {
	Now() time.Time
}

type realClock struct {}
func (realClock) Now() time.Time { return time.Now() }

func main() {
	CheckEndOfMonth(realClock{})
}
  • Package level time generator function
type nowFuncT func() time.Time

var nowFunc nowFuncT

func TestCheckEndOfMonth(t *Testing.T) {
    nowFunc = func() time.Time {
        return time.Now()
    }
    defer function() {
        nowFunc = time.Now
    }

    // Test your code here
}
  • Embed time generator in struct
type TimeValidator struct {
	// .. your fields
	clock func() time.Time
}

func (t TimeValidator) CheckEndOfMonth()  {
	x := t.now()
	// ...
}

func (t TimeValidator) now() time.Time  {
	if t.clock == nil {
		return time.Now() // default implementation which fall back to standard library
	}

	return t.clock()
}

Each has it's own plus and minus. Best way is to separate the function that generates the time and the processing part that uses the time.

This Post Stubing Time in golang goes into details about it and there is an example for making function with time dependency to be easily tested.

Solution 6 - Unit Testing

From Google result I found relatively simple solution: Here

The basic idea is using another function call "nowFunc" to get the time.Now(). In your main, initialize this function to return time.Now(). In your test, initialize this function to return a fixed fake time.

Solution 7 - Unit Testing

You can also use the faketime method used for the go playground. It will keep an internal "clock" value which replaces time.Now(), and will instantly return from any call to time.Sleep(), merely increasing the internal counter.

All calls to runtime.write (f.ex. fmt.Println) will be prefixed with the following header: \0 \0 P B <8-byte time> <4-byte data length> (big endian)

It was implemented here: https://github.com/golang/go/commit/5ff38e476177ce9e67375bd010bea2e030f2fe19

Using it is as simple as go run -tags=faketime test.go

Example test.go:

package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("Test!")
	time.Sleep(time.Second * 5)
	fmt.Println("Done.")
}

Output:

go run -v -tags=faketime scratch_22.go | hexdump -C

00000000  00 00 50 42 11 74 ef ed  ab 18 60 00 00 00 00 06  |..PB.t....`.....|
00000010  54 65 73 74 21 0a 00 00  50 42 11 74 ef ee d5 1e  |Test!...PB.t....|
00000020  52 00 00 00 00 06 44 6f  6e 65 2e 0a              |R.....Done..|
0000002c

However, I wouldn't recommend using this for actual unittests, as the change to runtime.write will probably have unintended consequences, breaking a lot of other things.

Solution 8 - Unit Testing

If you don't mind an extra dependency, there is an easy to use clock library for mocking time in Go, which wraps the standard library so it can be easily mocked in tests.

Solution 9 - Unit Testing

Same as the Flimsy's answer. Adding a concrete example.

Concept:

  • You can create a global function called CurrentTime to wrap the time.now()
  • Reassign the CurrentTime function in tests & make it return the desired value.
// main.go

package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Printf("This is the current year : %d ", GetCurrentYear())
}

// 'GetCurrentYear' function uses 'CurrentTime' function internally
func GetCurrentYear() int {
	return CurrentTime().Year()
}

var CurrentTime = func() time.Time {
	return time.Now()
}
// main_test.go

package main

import (
	"testing"
	"time"
	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type S struct{}

var _ = Suite(&S{})

func (s *S) TestCurrentYearShouldBeReturnedCorrectly(c *C) {
	expectedYear := 2022
	curentInstant := time.Date(expectedYear, 12, 01, 00, 00, 00, 0, time.UTC)

	// make 'CurrentTime' return hard-coded time in tests
	CurrentTime = func() time.Time {
		return curentInstant
	}

	c.Assert(GetCurrentYear(), Equals, expectedYear)

}

Here is the Go-Playground link

Solution 10 - Unit Testing

we can stub time.Now simply by using go package "github.com/undefinedlabs/go-mpatch".

Import the go-mpatch package and put the below code snippet in the code wherever you need to stub time.Now()

 mpatch.PatchMethod(time.Now, func() time.Time {
	return time.Date(2020, 11, 01, 00, 00, 00, 0, time.UTC)
})

Replace the values of time.Date as per your need.

checkout the sample code to check working of go-mpatch

go-playground sample

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsamolView Question on Stackoverflow
Solution 1 - Unit TestingnemoView Answer on Stackoverflow
Solution 2 - Unit TestingFlimzyView Answer on Stackoverflow
Solution 3 - Unit TestingAllen LuceView Answer on Stackoverflow
Solution 4 - Unit TestingdolzenkoView Answer on Stackoverflow
Solution 5 - Unit TestingahmyView Answer on Stackoverflow
Solution 6 - Unit TestingFeiView Answer on Stackoverflow
Solution 7 - Unit TestingcfstrasView Answer on Stackoverflow
Solution 8 - Unit TestingRuben SView Answer on Stackoverflow
Solution 9 - Unit TestingDhivya DandapaniView Answer on Stackoverflow
Solution 10 - Unit TestingSagar SonwaneView Answer on Stackoverflow