clockwork

A fake clock library for Go that makes testing time-dependent code deterministic and fast.

Library
Go
vv0.5.0
729stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
41/100Fair
Development Activity0
Maintenance20
Community56
Maturity60
Momentum28

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
82/100Excellent
Architecture88
Code Quality90
Innovation85
Learning Curve65

clockwork provides a Clock interface that Go packages can use instead of calling the time package directly, making chronology-dependent code testable. Production code accepts a clockwork.Clock, injects NewRealClock() to delegate to the standard time package, and tests inject a FakeClock that only advances when explicitly told to via Advance().

The FakeClock implementation tracks a list of waiters — timers, tickers, and Sleep/After calls — and lets tests deterministically control time without real delays or flaky sleep-based synchronization. BlockUntilContext lets a test wait until a goroutine under test has actually registered its timer before advancing the clock, avoiding races between goroutine startup and clock advancement.

What You Get

  • Clock interface - a drop-in replacement for time.After, time.Sleep, time.Now, time.NewTicker, time.NewTimer, and time.AfterFunc that production code can depend on instead of the time package directly
  • NewRealClock() - the production implementation, which simply delegates every call straight to the standard time package
  • NewFakeClock() / NewFakeClockAt() - a fully deterministic clock for tests, advanced manually with Advance(d) instead of waiting on real time
  • BlockUntil / BlockUntilContext - wait until a goroutine under test has registered the expected number of timers/tickers before advancing the clock, with context cancellation to avoid deadlocks
  • clockwork.WithDeadline / WithTimeout - context.Context constructors whose deadline is driven by a FakeClock, including a distinguishable ErrFakeClockDeadlineExceeded error

Common Use Cases

  • Testing timeout and retry logic without real sleeps
  • Testing scheduled or periodic jobs built on tickers
  • Deterministically ordering concurrent goroutines that depend on elapsed time
  • Verifying context deadline/cancellation behavior in code that builds contexts with fake time

Under The Hood

Architecture clockwork.go defines a single Clock interface with two implementations: realClock, which forwards every call directly to the time package, and FakeClock, which guards a sync.RWMutex-protected list of waiters ([]expirer) and blockers ([]*blocker) kept sorted by expiration. Advance() walks that sorted list, popping and firing any waiter whose expiration falls at or before the target time and re-inserting periodic waiters (tickers) via setExpirer, while ticker.go and timer.go implement the shared expirer interface (expire, expiration, setExpiration) for fakeTicker and fakeTimer, each backed by a buffered channel of size 1 so expiration sends never block. context.go layers clockwork.WithDeadline/WithTimeout on top of this by wrapping a fakeTimer’s channel inside a hand-rolled fakeClockContext that runs a dedicated cancellation goroutine selecting over the timer, an explicit cancel signal, and the parent context’s Done channel.

Tech Stack Written in Go 1.21+ using only the standard library — context, errors, fmt, slices, sync, and time — with zero third-party dependencies. There is no build tooling beyond go test; the module is consumed directly via Go modules and documented through pkg.go.dev-rendered godoc comments rather than a separate docs site.

Code Quality Tests are organized per source file (clockwork_test.go, context_test.go, ticker_test.go, timer_test.go, plus a runnable example_test.go) using only the standard testing package — no assertion library such as testify is used. CI (.github/workflows/ci.yaml) runs go test -v -race across three Go versions (1.21-1.23), and a separate CodeQL workflow performs static security analysis on every push and pull request. Errors are explicit and typed where it matters, such as the wrapped ErrFakeClockDeadlineExceeded sentinel, and concurrency primitives (sync.RWMutex, sync.OnceFunc) are used deliberately around every mutation of shared state. No dedicated linter configuration was found in the repository.

API Design The public API mirrors the standard time package almost one-to-one (After, Sleep, Now, Since, Until, NewTicker, NewTimer, AfterFunc), so adopting it requires threading a single Clock argument through existing code rather than learning new concepts. Every exported type and function carries a godoc comment, the deprecated BlockUntil is clearly marked in favor of the context-aware BlockUntilContext, and the surface area is intentionally minimal — there is nothing to configure beyond choosing NewRealClock or NewFakeClock.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search