A common testing mistake is reading the current time directly inside production code. Injecting a time function lets tests replace an unpredictable dependency with a deterministic implementation. section 11.6
Injecting a clock into Cache
type now func() time.Time type Cache struct { events []Event now now } func NewCache() *Cache { return &Cache{now: time.Now} } A test can assign a function returning a fixed timestamp, making TrimOlderThan reproducible. A mutable global such as var now = time.Now shares state across tests, prevents safe parallel execution, and weakens isolation. section 11.6
Choose the injection shape according to the dependency and API boundary:
- Keep time operations in injectable struct dependencies; add another function or group related operations behind an interface when needed. section 11.6
- Pass the current time, or a calculated cutoff time.Time, from the caller when exposing an unexported dependency is inconvenient; this avoids extra types and stubs. section 11.6
type Cache struct {
mu sync.RWMutex
events []Event
}
type Event struct {
Timestamp time.Time
Data string
}
func (c *Cache) TrimOlderThan(since time.Duration) {
c.mu.RLock()
defer c.mu.RUnlock()
t := time.Now().Add(-since) #1
for i := 0; i < len(c.events); i++ {
if c.events[i].Timestamp.After(t) {
c.events = c.events[i:] #2
return
}
}
}
Compare the direct time.Now call with the Cache operation that tests must make deterministic. section 11.6




















