testtime
package provides testtime.Now()
and testtime.SetTime()
.
testtime.SetTime()
stores a fixed time to a map with goroutine ID of testtime.SetTime()
as a key.
When goroutine ID of testtime.Now()
is related to a fixed time by testtime.SetTime()
, it returns the fixed time otherwise it returns current time which is returned by time.Now()
.
package main
import (
"fmt"
"time"
"testing"
"github.com/tenntenn/testtime"
)
func Test(t *testing.T) {
t.Run("A", func(t *testing.T) {
// set zero value
testtime.SetTime(t, time.Time{})
// true
if time.Now().IsZero {
t.Error("error")
}
})
t.Run("B", func(t *testing.T) {
// set func which return zero value
f := func() time.Time {
return time.Time{}
}
testtime.SetFunc(t, f)
// true
if time.Now().IsZero {
t.Error("error")
}
})
// false
if !time.Now().IsZero {
t.Error("error")
}
}
The testtime
command replace time.Now
to testtime.Now
.
It prints a file path of overlay JSON which can be given to -overlay
flag of go test
command.
$ go test -overlay=$(go run github.com/tenntenn/testtime/cmd/testtime@latest)
PASS
ok main 0.156s
You can install testtime
with the go install
command.
And don't forget to add the installed directory to the PATH environment variable.
$ go install github.com/tenntenn/testtime/cmd/testtime@latest
The testtime
command creates an overlay JSON file and time.go
which is replaced time.Now
in $GOPATH/pkg/testtime
directory. The testtime
command does not update these files without -u
flag.
$ cat `testtime` | jq
{
"Replace": {
"/usr/local/go/src/time/time.go": "/Users/tenntenn/go/pkg/testtime/time_go1.23.1.go"
}
}
$ diff /usr/local/go/src/time/time.go /Users/tenntenn/go/pkg/testtime/time_go1.23.1.go
94a95,96
> "runtime"
> "sync"
1159c1161
< func Now() Time {
---
> func _Now() Time {
1695a1698,1726
>
> // It will be added to GOROOT/src/time/time.go.
>
> //go:linkname timeMap
> var timeMap sync.Map
>
> // Now returns a fixed time which is related with the goroutine by SetTime or SetFunc.
> // If the current goroutine is not related with any fixed time or function, Now calls time.Now and returns its returned value.
> func Now() Time {
> v, ok := timeMap.Load(goroutineID())
> if ok {
> return v.(func() Time)()
> }
> return _Now()
> }
>
> func goroutineID() string {
> var buf [64]byte
> n := runtime.Stack(buf[:], false)
> // 10: len("goroutine ")
> for i := 10; i < n; i++ {
> if buf[i] == ' ' {
> return string(buf[10:i])
> }
> }
> return ""
> }
>
> // End of testtime's code
See _examples directory.