clockwork
A fake clock library for Go that makes testing time-dependent code deterministic and fast.
Repository Health
Technical Analysis
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.
Used by 3 apps in this directory
Teleport
Security · Authentication
Zero-trust infrastructure access platform that replaces credentials and VPNs with short-lived certificates, SSO, and identity-aware proxies for SSH, Kubernetes, databases, RDP, and AI agents.
Weaviate
Databases · Search
Open-source vector database combining semantic search, hybrid queries, RAG, and image search in a single cloud-native system built for production scale.
ZITADEL
Authentication
Open-source, API-first identity platform delivering multi-tenancy, Passkeys, OIDC, SAML, and SCIM without vendor lock-in.