Overview
Consider the following example where subtests using t.Run
make t.Parallel
calls to run in parallel:
The output is as follows:
=== RUN TestFoo
a_test.go:6: TestFoo entered
=== RUN TestFoo/Sub1
=== PAUSE TestFoo/Sub1
=== RUN TestFoo/Sub2
=== PAUSE TestFoo/Sub2
=== NAME TestFoo
a_test.go:21: TestFoo exited
=== CONT TestFoo/Sub1
=== CONT TestFoo/Sub2
a_test.go:18: Sub2 entered
a_test.go:20: Sub2 exited
=== NAME TestFoo/Sub1
a_test.go:12: Sub1 entered
a_test.go:14: Sub1 exited
--- PASS: TestFoo (0.00s)
--- PASS: TestFoo/Sub2 (0.00s)
--- PASS: TestFoo/Sub1 (0.00s)
PASS
As seen, the deferred log in TestFoo
executes before either Sub1
or Sub2
finish running
The issue with Go’s t.Parallel()
is that it requires an initial pass to determine which tests can run in parallel, pausing the test goroutines and continuing them later. When used in subtests, the top-level test may finish and execute its defer
statements while subtests are still paused, which is exactly what happens in our case
The solution is to use t.Cleanup
, which, unlike defer
, ensures the correct execution order even with t.Parallel()
. It guarantees that cleanup tasks are run at the right time, after the parallel subtests have finished