Concurrency · 3 MIN READ
Test time and cancellation without sleeping on the wall clock
Use testing/synctest to assert a concurrent timeline deterministically.
Tests that sleep for a guessed interval are slow and sensitive to machine load. testing/synctest is a standard package in Go 1.25 and later. It runs a test bubble with virtual time and coordinated goroutine waiting. It is useful for in-process protocols, not a simulator for a remote database or every operating-system event.
Test time and cancellation without sleeping on the wall clock · A MAP OF THE IDEA
- Describe a timeline
- Hold a known phase
- Advance virtual time
- Assert completion and effects
Useful when
- Use testing/synctest to assert a concurrent timeline deterministically.
- Describe the timeline and assert both early and final states. Virtual time makes supported concurrent tests reproducible; it does not replace real boundary integration checks.
Watch out
- Use the bubble for a deterministic in-process protocol and retain isolated integration tests for external services.
Implementation & operation cost: Choose the policy from the actual contract, then verify its failure path in the target environment.
State the timeline first
Suppose work waits five seconds, publishes a result and exits. The test should establish that it has not published before the deadline and has completed after it. synctest.Wait lets in-bubble goroutines settle at supported blocking points so the assertions do not depend on the scheduler getting lucky.
package timeline
import (
"testing"
"testing/synctest"
"time"
)
func TestDelayedResult(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
done := make(chan struct{})
go func() { time.Sleep(5 * time.Second); close(done) }()
synctest.Wait()
select {
case <-done:
t.Fatal("completed before the deadline")
default:
}
time.Sleep(5 * time.Second)
synctest.Wait()
select {
case <-done:
default:
t.Fatal("result not published")
}
})
}
Save this complete example as timeline_test.go in a native Go 1.25+ module and run go test -race. The five-second sleep advances virtual time instead of imposing five seconds of wall-clock waiting. This lesson is a reading; it does not promise this diagnostic package on every WASI target.
Add a cancellation branch
Model a worker waiting on either its timer or ctx.Done, then cancel at a known phase. Assert that the worker closes its completion signal and does not publish an unwanted result. Cancellation and a simultaneously ready result still need a documented policy; select is not a strict priority rule.
Know the boundary of the bubble
Virtual time advances when the package can establish the relevant in-bubble blocking state. External network servers, syscalls and objects outside the bubble have different constraints. Keep real integration tests for sockets and databases rather than treating a fast virtual-time test as evidence of remote behavior.
Keep the invariant after removing sleeps
A deterministic test must still assert something meaningful. For a retry protocol, count durable effects and total attempts under a deadline; for cancellation, prove exit with a completion signal. A test that merely waits for the bubble to finish does not verify every business result.
TAKE THIS WITH YOU
Describe the timeline and assert both early and final states. Virtual time makes supported concurrent tests reproducible; it does not replace real boundary integration checks.
Check your understanding
A virtual-time unit test passes. What can it establish about a remote PostgreSQL failover?
Go to the source
Progress stays in this browser. No account needed.