In general, libraries should offer structs, and you should declare your own interfaces with the subset of methods that you require.
This is somewhat messy here because the timer channel is accessed by a field rather than a method, but you could write a struct that embeds time.Timer and adds C() as a method. Then both your wrapped time.Timer and your mock/fake could fulfill your interface.
Defining interfaces around existing types works, but quickly falls apart as soon as methods return concrete types. In those cases you need to write more complex wrappers, which is often not worth the hassle.
Can you expand on this? I find time.After super useful for time arithmetic in control flow, like checking for expiry, validating leases, etc.
I’m talking about the free function After() that’s part of the time package:
https://golang.org/pkg/time/#After
That one can be used to create a channel that triggers after a certain amount of time. The reason you shouldn’t be using that one is because you can’t clean it up before it triggers. This means that any code that bails out early ends up leaking memory temporarily.
- Even though Clock is an interface, Timer is not. This means that if you want to use that package in combination with gomock, you're out of luck.
- The Clock interface doesn't provide a wrapper for context.WithTimeout(), which also depends on the system clock.
- Nit: The Clock interface also exposes functions like time.After(), which should in my opinion never be used in production code, as they don't support efficient cancelation. You should use timers instead.
In the end I resorted to writing my own set of interfaces.
Code: https://github.com/buildbarn/bb-storage/tree/master/pkg/cloc...
Documentation: https://pkg.go.dev/github.com/buildbarn/bb-storage/pkg/clock