The Testing Package Methods in Go You Keep Forgetting
A reference to the testing package methods Go developers forget, grouped by job, with a run showing t.Name, t.Context, Failed, Skip and b.Loop in action.

The testing package is small, but it has enough methods that most of us use five and forget the rest. This is a reference, grouped by what you are trying to do, with the Go version where a method is newer. Where behaviour is easy to get wrong, it was checked in a short run, shown below. Pair it with the articles on first tests, table tests and subtests. Versions: Go 1.25.
Tested with Go 1.25.6, macOS on Apple Silicon (arm64)
package main
import (
"context"
"testing"
"time"
)
func TestMethodsTour(t *testing.T) {
t.Logf("Name: %s", t.Name())
_, hasDeadline := t.Deadline()
t.Logf("Deadline set by -timeout: %v", hasDeadline)
t.Logf("Short: %v, Verbose: %v", testing.Short(), testing.Verbose())
ctx := t.Context() // Go 1.24: cancelled just before cleanups run
t.Cleanup(func() {
t.Logf("in cleanup, t.Context is done: %v", ctx.Err() == context.Canceled)
})
t.Logf("in the body, t.Context is done: %v", ctx.Err() != nil)
t.Run("child name", func(t *testing.T) {
t.Logf("Name of a subtest: %s", t.Name())
t.Error("this marks only the child as failed")
})
t.Logf("parent Failed() after a failing child: %v", t.Failed())
t.Logf("a nil error check that does not fail: %v", ctx.Err() == nil)
}
func TestSkipAndFailNow(t *testing.T) {
t.Skipf("skipping with a reason: %s", "no database configured")
t.Fatal("never reached")
}
func BenchmarkLoop(b *testing.B) {
sum := 0
for b.Loop() { // Go 1.24: the loop form that keeps setup out of the timing and the compiler honest
sum += len("benchmark")
}
_ = sum
}
func TestDeadlineHelper(t *testing.T) {
// A test that must finish before the package timeout can derive its own deadline.
ctx, cancel := context.WithTimeout(t.Context(), 50*time.Millisecond)
defer cancel()
select {
case <-ctx.Done():
t.Logf("derived context ended: %v", ctx.Err())
case <-time.After(time.Second):
t.Fatal("timeout did not fire")
}
}package main
func main() {}strip() { sed -E 's/ \([0-9.]+s\)//; s/[[:space:]]+[0-9.]+s$//'; }
go test -count=1 -v -run 'TestMethodsTour|TestSkipAndFailNow|TestDeadlineHelper' . 2>&1 | grep -E '^( *--- | methods_test| methods_test|ok|FAIL)' | strip
echo '--- benchmark with b.Loop'
go test -run '^$' -bench BenchmarkLoop -benchtime=100000x . 2>&1 | grep -E '^Benchmark' | sed -E 's/[0-9.]+ ns\/op/N ns\/op/; s/-[0-9]+[[:space:]]+/ /' methods_test.go:10: Name: TestMethodsTour
methods_test.go:12: Deadline set by -timeout: true
methods_test.go:13: Short: false, Verbose: true
methods_test.go:19: in the body, t.Context is done: false
methods_test.go:22: Name of a subtest: TestMethodsTour/child_name
methods_test.go:23: this marks only the child as failed
methods_test.go:25: parent Failed() after a failing child: true
methods_test.go:26: a nil error check that does not fail: true
methods_test.go:17: in cleanup, t.Context is done: true
--- FAIL: TestMethodsTour
--- FAIL: TestMethodsTour/child_name
methods_test.go:30: skipping with a reason: no database configured
--- SKIP: TestSkipAndFailNow
methods_test.go:48: derived context ended: context deadline exceeded
--- PASS: TestDeadlineHelper
FAIL
FAIL example.test/lab
FAIL
--- benchmark with b.Loop
BenchmarkLoop 100000 N ns/opFailing and reporting
| Method | What it does |
|---|---|
t.Error(args...), t.Errorf(format, args...) | Log and mark the test failed; keep running |
t.Fatal(args...), t.Fatalf(...) | Log, mark failed, stop this test now |
t.Fail() / t.FailNow() | Mark failed (and stop, for FailNow) without a message |
t.Log(args...), t.Logf(...) | Log; shown on failure or with -v |
t.Failed() | Has this test (or a subtest) failed so far? |
t.Skip(args...), t.Skipf(...), t.SkipNow() | Stop and mark skipped |
t.Skipped() | Was it skipped? |
Fatal and FailNow use runtime.Goexit, so they must be called from the goroutine that runs the test. From another goroutine, use Error and signal the test function instead. The run shows t.Failed() returning true on the parent after a child's t.Error: a failed subtest fails its parent, while the parent's own body continues. t.Skipf stops the test and records the reason, and the output ends as --- SKIP: TestSkipAndFailNow; the t.Fatal after it never ran.
Structure
| Method | What it does |
|---|---|
t.Run(name, f) | Run a subtest; returns whether it passed |
t.Parallel() | Pause until the parent returns, then run alongside other parallel tests |
t.Name() | The full name, with slashes for subtests |
t.Helper() | Report failures at the caller's line |
t.Name() inside the subtest printed TestMethodsTour/child_name, with the space in the name turned into an underscore. Use it to build unique file or table names in helpers. For details on ordering and the loop variable, see subtests and parallel tests, and for line numbers t.Helper.
State, files and the environment
| Method | What it does |
|---|---|
t.Cleanup(f) | Run f when the test ends, last-in first-out |
t.TempDir() | A unique directory, removed automatically |
t.Setenv(k, v) | Set an environment variable, restore it at the end (not with Parallel) |
t.Chdir(dir) | Change directory, restore it at the end (Go 1.24, not with Parallel) |
t.Context() | A context cancelled just before cleanups run (Go 1.24) |
t.Context() is the one people miss. The run logged that it is not done in the test body and is done inside a cleanup: it is cancelled after the test function returns and before the registered cleanups, so goroutines you start with it stop first, and a cleanup that waits for them will not hang. Use it for servers, workers and clients that must not outlive the test. Setenv and Chdir are process-wide, which is why they refuse to run in parallel tests (cleanup details, environment variables).
Time and limits
| Method | What it does |
|---|---|
t.Deadline() | When the test binary's timeout expires, and whether one is set |
testing.Short() | True with -short; call after flags are parsed |
testing.Verbose() | True with -v |
testing.Testing() | True inside a test binary, so library code can detect it |
The run printed Deadline set by -timeout: true because go test always sets a timeout (10 minutes by default; -timeout 0 disables it). A test that talks to something slow can derive its own limit from t.Context(), as TestDeadlineHelper does: a 50 ms timeout ended the wait with context deadline exceeded. Use testing.Short() to skip slow or external tests in the quick loop.
For tests that involve time itself, Go 1.25 added the testing/synctest package, which runs code in a bubble with a fake clock so timers and sleeps run instantly (testing with synctest covers when it fits).
Benchmarks
| Method | What it does |
|---|---|
for b.Loop() { ... } | The benchmark loop (Go 1.24); setup before it is not timed |
for range b.N | The older form; you must manage the timer yourself |
b.ResetTimer(), b.StopTimer(), b.StartTimer() | Control what is measured |
b.ReportAllocs() | Report allocations (or use -benchmem) |
b.SetBytes(n) | Report throughput in MB/s |
b.RunParallel(f) | Run the body from many goroutines |
b.Run(name, f) | Sub-benchmarks |
b.ReportMetric(v, unit) | Add a custom metric |
b.Loop() ran exactly 100,000 iterations when the command asked for -benchtime=100000x. Besides being shorter than the b.N form, it keeps setup out of the timing automatically and prevents the compiler from optimising away the loop body. See benchmarking a handler for a full example.
Fuzzing and examples
f.Add(args...) seeds the corpus and f.Fuzz(func(t *testing.T, args...) {...}) defines the property to check; run with go test -fuzz=FuzzName. Examples are functions named ExampleX with an // Output: comment that go test compiles and verifies, and pkg.go.dev shows as documentation.
Handy flags
-run, -skip, -v, -count=1, -race, -cover, -shuffle=on, -failfast, -short, -timeout, -parallel N, -bench, -benchmem, -benchtime, -fuzz, -fuzztime, -list. The testing flags reference has the full list, and the package documentation covers every method above.


