upvote
That is a useful pattern, though I was unclear on why `t.Cleanup` and not `defer`. In case others are curious, too:

> Parallel subtestsWith t.Run(..., func(t testing.T) { t.Parallel(); ... }), the parent test function can return (and thus run its defers) before parallel subtests actually finish.*

reply
Short version is this:

If you are going to get into the business of introducing order dependence to test cases through global state (see my other reply on the parent), you will always want the cleanup to work correctly.

1. Using (testing.TB).Cleanup is a good defensive habit to have if you author test helpers, especially if the test helpers (see: (testing.TB).Helper) themselves do something (e.g., resource provisioning) that requires ordered teardown. Using (testing.TB).Cleanup is better than returning a cancellation or cleanup function from them.

2. (testing.TB).Cleanup has stronger guarantees about when it is called, especially when the test case itself crashes. Example: https://go.dev/play/p/a3j6O9RK_OK.

I am certain that I am forgetting another edge case or two here.

Generally nobody should be designing their APIs to be testable through mutable global state. That solves half the problem here.

reply
reply
Its unexported for that reason. You only change it in tests.
reply
Just for the record - this is package local - it's fine within the package it is defined in, but no other package will use the implementation, they will all use the standard library.

Others have linked to the much more "fun" https://github.com/bouk/monkey which is an actual monkey patch, in that it changes the code that is called from anywhere in the runtime

reply
The point of the OP is that it changes calls to `time.Now` regardless of whether the code that's calling it uses your variable or not.
reply
I suspect that using a build tag (say `test`) and two function definitions (one that directly calls `time.Now()` and one test-only one that uses a mutable var) will optimize out to zero cost in the non-test case - last I fiddled with that, it was pretty good at consistently inlining trivial wrapper funcs like that.
reply
The compiler will only use _test.go files in the test build - so not an explicit build tag, but a built in one.
reply
That doesn't give you a way to exclude conflicting code, unfortunately, so you can't provide an optimal one for non-test code with it.

And stuff like `func SetTime(...)` in a _test.go file only works for tests in that same package, because other packages don't compile that _test.go and won't have that function defined.

reply
I'm not sure that we are on the same page

Are you saying that you want multiple build tagged files each with a different implementation of the function, all in the same package? (eg. windows, linux, arm)

I mean, the example given by the GP is two implementations in the same package, the standard library version is used in the prod file and the test implementation in the test files - the _test.go is the (implicit) build tag

Or do you have something else in mind?

reply
deleted
reply