85

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 the time interface, and during the test I used 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.

Is there an alternative way to stub out time.Now() globally? Maybe making a system call to change the system clock?

Can we maybe 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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
samol
  • 18,950
  • 32
  • 88
  • 127

12 Answers12

91

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, fewer side effects means that it is much easier to make the code run concurrently.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
nemo
  • 55,207
  • 13
  • 135
  • 135
  • 1
    @stephanos you should post this as separate answer as it's easy to miss. Also usage examples on the repo itself would be useful. – Kamil Dziedzic Mar 28 '16 at 10:57
  • The disadvantage of this is that if the usecase requirese multiple time, we need to create a struct for each time. example `midnightClock` that returns time at midnight, `christmass2015Clock` that returns specific time – ahmy Sep 28 '19 at 08:03
  • Full example of how to have a concrete implementation that uses the clock interface in production and test code would be helpful for reference – IceFire Jun 09 '22 at 09:14
53

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"
)

func TestFoo(t *testing.T) {
    now = func() time.Time { return ... }
    // The rest of your test
}
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
  • This is very interesting solution! In what file would you put the `var now = time.Now()` ? Would it be in a specific `package_variables`-file? – Brainmaniac Jun 26 '23 at 08:38
  • Thanks for your answers. I now understand why my edit was wrong (https://chat.openai.com/share/7daf9681-cb17-4e76-a4a6-026a833b4c29) My question of where to put the code was meant to ask *where in the package "folder"* should I declare the `var = time.Now` ? In its own file or in any file of the package? – Brainmaniac Jun 26 '23 at 13:26
  • Also, if I put the proposed code in a test it will just say now is never used. – Brainmaniac Jun 26 '23 at 13:28
  • You can put it wherever you want. I would not use a separate file just for that one line of code. But you can if you think it helps for some reason. – Jonathan Hall Jun 27 '23 at 07:52
  • It will only say it's never used if you never use it. You _should_ be using it in your main code... otherwise you gain no advantage from stubbing out time in your code. – Jonathan Hall Jun 27 '23 at 07:53
  • @Brainmaniac: If you can paste a link to a gist of your code, I can probably offer more specific suggestions. – Jonathan Hall Jun 27 '23 at 07:53
28

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 dependency injection (DI) pattern. Production code stays separate from test code and you gain useful control of system dependencies.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Allen Luce
  • 7,859
  • 3
  • 40
  • 53
  • 29
    Note: You shouldn't use this lib in prod due to its license https://github.com/bouk/monkey/blob/master/LICENSE.md – kucherenkovova Nov 21 '18 at 16:27
  • 45
    You shouldn't do this in production regardless of the license. – Allen Luce Nov 21 '18 at 19:02
  • 3
    @AllenLuce is that why the license looks the way it does? Seems quite a hostile license – Rambatino Jun 24 '19 at 07:18
  • 1
    @Rambatino I didn't write the license so I can't give you a real answer. There are plenty of non-license-related reasons to keep this out of production code: it's incompatible with inlines and is not thread-safe. And redefining library functions is likely to confuse anyone who has to deal with your mess later. But used judiciously within a test context this package can make your production code simpler than any other technique described in the answers to this question. – Allen Luce Jun 24 '19 at 18:15
  • 2
    I'm aware of all that - but "I do not give anyone permissions to use this tool for any purpose" - does that not imply do not use it for testing? It just confuses me why that would be included in a package that has clearly been released for people to use? – Rambatino Jun 24 '19 at 23:30
  • As far as understand this solution it won't work reliably when tests using this patching approach are executed in parallel. – yaccob Sep 19 '19 at 19:03
  • 1
    Yup, it's not thread safe so any tests that rely on it must be run serially. It also doesn't work with inlining. My standard test command: `go test -gcflags=-l -p 1 ./...` – Allen Luce Sep 20 '19 at 17:30
  • the package maintainer seems to have some clarification about the license: https://github.com/bouk/monkey/pull/18#issuecomment-383364442 – Sahas Nov 02 '20 at 12:52
  • Neat solution BUT I recommend against it: that module has been archived, and the author states in the license file "I do not give anyone permissions to use this tool for any purpose. Don't use it." That is as unambiguous as it gets. Also the approach is not thread-safe, and (although clever) is quite invasive. – Oliver Dec 12 '22 at 23:21
10

Also if you need to just stub time.Now you can inject the 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 a dynamic languages background (e.g., Ruby) :(

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ev Dolzhenko
  • 6,100
  • 5
  • 38
  • 30
7

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 its own pluses and minuses. The best way is to separate the function that generates the time and the processing part that uses the time.

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ahmy
  • 4,095
  • 4
  • 27
  • 36
5

This is the same as Jonathan Hall's answer, but I am adding a concrete example.

Concept:

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

File 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()
}

File 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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dhivya Dandapani
  • 401
  • 1
  • 5
  • 8
4

We can stub time.Now simply by using the Go package 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.

check out the sample code to check the working of go-mpatch.

go-playground sample

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
3

I found a relatively simple solution here. The basic idea is using another function called "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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Fei
  • 1,906
  • 20
  • 36
  • Im also using this method but wonder if there's any downside to the approach. The nice thing about this is it avoids having to pass a `Clock` interface around. – sthomps Nov 02 '18 at 17:04
3

You can also use the faketime method used for 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 (for example, 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 unit tests, as the change to runtime.write will probably have unintended consequences, breaking a lot of other things.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
cfstras
  • 1,613
  • 15
  • 21
0

The simple alternative is you can use sqlmock.AnyArg() to pass time.Now() as an argument.

Example

If the query is

[sqlBuilder.Update][2](tableName).Set("last_updated", time.Now()).Where(sq.Eq{"id": id}).ToSql()

and you want to mock this, do

sqlMock.ExpectExec("UPDATE tableName SET last_updated = ? WHERE id = ?").WithArgs(sqlmock.AnyArg())

instead of

sqlMock.ExpectExec("UPDATE tableName SET last_updated = ? WHERE id = ?").WithArgs(time.Now())
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
0

What works for me is a small struct

package clock

import "time"

type Clock struct {
    MockTime time.Time
}

func (c Clock) Now() time.Time {
    if c.MockTime.IsZero() {
        return time.Now() // use default golang
    } else {
        return c.MockTime
    }
}

Embed the Clock struct in your struct as a dependency, or pass it along as function parameter.

ahmy
  • 4,095
  • 4
  • 27
  • 36
0

You can use agiledragon/gomonkey, it's similar to bouk/monkey package. Use like this:

package foo

import (
    "testing"
    "github.com/agiledragon/gomonkey/v2"
)

func TestAny(t *testing.T) {
    patches := gomonkey.ApplyFunc(time.Now, func() time.Time {
        return time.Unix(1615256178, 0)
    })
    defer patches.Reset()
    ...
}
Alan
  • 7
  • 1