Go

Compare t.Cleanup, defer, and TestMain for setup/teardown. When is each appropriate?

Question 381MediumGo 1.22 to 1.25
  • defer: fine in a test without parallel subtests; runs when the function returns.
  • t.Cleanup(fn): runs after the test and all its subtests complete, in LIFO order. Crucially, helpers can register cleanup so callers don't need to remember — the pattern behind t.TempDir().
  • TestMain(m): per-package, once. For expensive shared resources (start a Docker container, compile a binary, parse flags).
func newTestServer(t *testing.T) *httptest.Server {
    t.Helper()
    srv := httptest.NewServer(newRouter())
    t.Cleanup(srv.Close) // caller can't forget
    return srv
}

var pgURL string

func TestMain(m *testing.M) {
    flag.Parse()
    if testing.Short() {
        os.Exit(m.Run()) // skip heavy setup
    }
    stop, url := startPostgres()
    pgURL = url
    code := m.Run()
    stop() // os.Exit skips defers, so tear down explicitly
    os.Exit(code)
}

Gotchas: os.Exit doesn't run deferred functions, so a defer stop() in TestMain followed by os.Exit leaks the resource. Since Go 1.15 you may simply return from TestMain and the exit code is propagated. t.Helper() makes failures report the caller's line, not the helper's.

More on Performance, Profiling & Testing

All 38 Performance, Profiling & Testing questions