Related
I've written a Go application, and all of the packages have full test coverage. I'm in the process of writing my main package - which will handle all of the initial setup for the application in the main() function - this function currently reads in 14 environment variables and then sets the relevant variable in the application. A simple overview of the code is:
func main() {
myStruct1 := privatePackage.myStructType{}
myStruct2 := publicPackage.otherStructType{}
if config1 := os.Getenv("CONFIG_FOO"); config1 != "" {
myStruct1.attribute1 = config1
}
// ....
if config14 := os.Getenv("CONFIG_BAR"); config14 != "" {
myStruct2.attribute5 = config14
}
}
When I test unit env variables/OS args, I typically just set the env variable directly in the test function - so something like:
func TestMyArgument(t *testing.T) {
os.Setenv("CONFIG_BAZ", "apple")
//Invoke function that depends on CONFIG_BAZ
//Assert that expected outcome occurred
}
I pretty much always use table-driven tests, so the above snippet is a simplified example.
The issue is that my main() function takes in 14 (and growing) env variables, and whilst some env variables are essentially enums (so there's a small number of valid options - for example there's a small number of database drivers to choose from), other env variables have virtually unlimited potential values. So how can I effectively cover all of the (or enough of the) permutations of potential configs?
EDIT: When this application is deployed, it's going into a K8s cluster. Some of these variables are secrets that will be pulled in from secure store. Using a JSON file isn't viable because some of the values need to be encrypted/changed easily.
Also, using a JSON file would require me to store this file and share it between hundreds/thousands of running pods - this storage would then act as a point of failure.
To clarify, this question isn't about env vars VS config files; this question is about the best way to approach testing when there's a significant number of configurable variables - with each variables having a vast number of potential values - resulting in thousands of possible configuration permutations. How do I guarantee sufficient test coverage in such a scenario?
#Steven Penny is right: uses json
and use reflect can make the code more simple:
package main
import (
"encoding/json"
"fmt"
"os"
"reflect"
"strconv"
)
type MyStructType struct {
Attribute1 string `json:"CONFIG_FOO"`
Attribute2 string `json:"CONFIG_BAZ"`
Attribute3 int `json:"CONFIG_BAR"`
}
func NewMyStructTypeFormEnv() *MyStructType {
myStructType := MyStructType{}
ReflectMyStructType(&myStructType)
fmt.Println("myStructType is now", myStructType)
return &myStructType
}
func NewMyStructTypeFormJson() *MyStructType {
myStructType := MyStructType{}
f, e := os.Open("file.json")
if e != nil {
panic(e)
}
defer f.Close()
json.NewDecoder(f).Decode(&myStructType)
fmt.Println("myStructType is now", myStructType)
return &myStructType
}
func ReflectMyStructType(ptr interface{}){
v := reflect.ValueOf(ptr).Elem()
fmt.Printf("%v\n", v.Type())
for i := 0; i < v.NumField(); i++ {
env_str := v.Type().Field(i).Tag.Get("json")
if(env_str == ""){continue}
if config := os.Getenv(env_str); config != "" {
if v.Field(i).Kind() == reflect.String{
v.Field(i).SetString(config)
}else if v.Field(i).Kind() == reflect.Int{
iConfig,_ := strconv.Atoi(config)
v.Field(i).SetInt(int64(iConfig))
}
}
}
}
func main() {
NewMyStructTypeFormJson()
os.Setenv("CONFIG_FOO", "apple")
os.Setenv("CONFIG_BAZ", "apple")
os.Setenv("CONFIG_BAR", "1")
NewMyStructTypeFormEnv()
}
Beyond one or two, I don't think using environment variables is the right approach, unless it's required (calling something with os/exec). Instead, would be better to read from a config file. Here is an example with JSON:
{
"CONFIG_BAR": "east",
"CONFIG_BAZ": "south",
"CONFIG_FOO": "north"
}
package main
import (
"encoding/json"
"fmt"
"os"
)
func main() {
f, e := os.Open("file.json")
if e != nil {
panic(e)
}
defer f.Close()
var s struct { CONFIG_BAR, CONFIG_BAZ, CONFIG_FOO string }
json.NewDecoder(f).Decode(&s)
// {CONFIG_BAR:east CONFIG_BAZ:south CONFIG_FOO:north}
fmt.Printf("%+v\n", s)
}
TOML would be a good choice as well.
https://golang.org/pkg/encoding/json
https://pkg.go.dev/github.com/pelletier/go-toml
I saw many blogs where they write how to do unit testing but I will understand theory portion and not understand how to implement the test case can anyone will tell me that how will I implement the first test case for more understanding with the unit testing. Here I'm implementing the small program for average:-
Folder structure is:-
main.go
average(Folder)----> math_test.go
code in the both file is:-
main.go
package main
import "fmt"
import "testcases/average"
func main() {
xs := []float64{1,2,3,4}
avg := m.Average(xs)
fmt.Println(avg)
}
math_test.go
package math
import "testing"
func TestAverage(t *testing.T) {
var v float64
v = Average([]float64{1,2})
if v != 1.5 {
t.Error("Expected 1.5, got ", v)
}
}
ERROR:- go build testcases/average: no non-test Go files in /home/iron/go/src/testcases/average
Helping me I'm very thankful to you.
You have to change the package name with the main or math as #mkopriva and #whitespace said and place them into the same folder see in example
main.go
package main
func Sum(x int, y int) int {
return x + y
}
func main() {
Sum(5, 5)
}
math_test.go
package main
import "testing"
func TestSum(t *testing.T) {
total := Sum(5, 5)
if total != 10 {
t.Errorf("Sum was incorrect, got: %d, want: %d.", total, 10)
}
}
Output:-
PASS
ok testcases 0.001s
And also a basic example of the testing.
this is because your test file is in package main. You can either change your main.go to have package math, or change your math_test.go to package main. Check my git repo, here I have not written any main function. Yet functions are written, and their output is checked with testing package.
For testing functions I could select which will run by option -run.
go test -run regex
Very common if we have dozens test cases is put it into array in order not to write function for each of that:
cases := []struct {
arg, expected string
} {
{"%a", "[%a]"},
{"%-a", "[%-a]"},
// and many others
}
for _, c := range cases {
res := myfn(c.arg)
if res != c.expected {
t.Errorf("myfn(%q) should return %q, but it returns %q", c.arg, c.expected, res)
}
}
This work good, but problem is with maintanance. When I add a new testcase, while debugging I want to start just a new test case, but I cannot say something like:
go test -run TestMyFn.onlyThirdCase
Is there any elegant way, how to have many testcases in array together with ability to choose which testcase will run?
With Go 1.6 (and below)
This is not supported directly by the testing package in Go 1.6 and below. You have to implement it yourself.
But it's not that hard. You can use flag package to easily access command line arguments.
Let's see an example. We define an "idx" command line parameter, which if present, only the case at that index will be executed, else all test cases.
Define flag:
var idx = flag.Int("idx", -1, "specify case index to run only")
Parse command line flags (actually, this is not required as go test already calls this, but just to be sure / complete):
func init() {
flag.Parse()
}
Using this parameter:
for i, c := range cases {
if *idx != -1 && *idx != i {
println("Skipping idx", i)
continue
}
if res := myfn(c.arg); res != c.expected {
t.Errorf("myfn(%q) should return %q, but it returns %q", c.arg, c.expected, res)
}
}
Testing it with 3 test cases:
cases := []struct {
arg, expected string
}{
{"%a", "[%a]"},
{"%-a", "[%-a]"},
{"%+a", "[%+a]"},
}
Without idx parameter:
go test
Output:
PASS
ok play 0.172s
Specifying an index:
go test -idx=1
Output:
Skipping idx 0
Skipping idx 2
PASS
ok play 0.203s
Of course you can implement more sophisticated filtering logic, e.g. you can have minidx and maxidx flags to run cases in a range:
var (
minidx = flag.Int("minidx", 0, "min case idx to run")
maxidx = flag.Int("maxidx", -1, "max case idx to run")
)
And the filtering:
if i < *minidx || *maxidx != -1 && i > *maxidx {
println("Skipping idx", i)
continue
}
Using it:
go test -maxidx=1
Output:
Skipping idx 2
PASS
ok play 0.188s
Starting with Go 1.7
Go 1.7 (to be released on August 18, 2016) adds the definition of subtests and sub-benchmarks:
The testing package now supports the definition of tests with subtests and benchmarks with sub-benchmarks. This support makes it easy to write table-driven benchmarks and to create hierarchical tests. It also provides a way to share common setup and tear-down code. See the package documentation for details.
With that, you can do things like:
func TestFoo(t *testing.T) {
// <setup code>
t.Run("A=1", func(t *testing.T) { ... })
t.Run("A=2", func(t *testing.T) { ... })
t.Run("B=1", func(t *testing.T) { ... })
// <tear-down code>
}
Where the subtests are named "A=1", "A=2", "B=1".
The argument to the -run and -bench command-line flags is a slash-separated list of regular expressions that match each name element in turn. For example:
go test -run Foo # Run top-level tests matching "Foo".
go test -run Foo/A= # Run subtests of Foo matching "A=".
go test -run /A=1 # Run all subtests of a top-level test matching "A=1".
How does this help your case? The names of subtests are string values, which can be generated on-the-fly, e.g.:
for i, c := range cases {
name := fmt.Sprintf("C=%d", i)
t.Run(name, func(t *testing.T) {
if res := myfn(c.arg); res != c.expected {
t.Errorf("myfn(%q) should return %q, but it returns %q",
c.arg, c.expected, res)
}
})
}
To run the case at index 2, you could start it like
go test -run /C=2
or
go test -run TestName/C=2
I wrote a simple code, that work fine with both, although with a bit different command line options. Version for 1.7 is:
// +build go1.7
package plist
import "testing"
func runTest(name string, fn func(t *testing.T), t *testing.T) {
t.Run(name, fn)
}
and 1.6 and older:
// +build !go1.7
package plist
import (
"flag"
"testing"
"runtime"
"strings"
"fmt"
)
func init() {
flag.Parse()
}
var pattern = flag.String("pattern", "", "specify which test(s) should be executed")
var verbose = flag.Bool("verbose", false, "write whether test was done")
// This is a hack, that a bit simulate t.Run available from go1.7
func runTest(name string, fn func(t *testing.T), t *testing.T) {
// obtain name of caller
var pc[10]uintptr
runtime.Callers(2, pc[:])
var fnName = ""
f := runtime.FuncForPC(pc[0])
if f != nil {
fnName = f.Name()
}
names := strings.Split(fnName, ".")
fnName = names[len(names)-1] + "/" + name
if strings.Contains(fnName, *pattern) {
if *verbose {
fmt.Printf("%s is executed\n", fnName)
}
fn(t)
} else {
if *verbose {
fmt.Printf("%s is skipped\n", fnName)
}
}
}
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.
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.
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
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.
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) :(
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.
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
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.
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.
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())
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.
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.
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.
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.
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
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.
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) :(
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.
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
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.
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.
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())
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.
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.