Hands-on study of table-driven tests and test organization in Go — the idiomatic testing pattern used throughout the Go standard library.
- Table-driven tests with
t.Runand descriptive subtest names - Parallel subtests with
t.Parallel()and the loop closure pitfall (Go < 1.22) - Test helpers with
t.Helper()for correct error line reporting - Setup and teardown with
t.Cleanup - Black-box testing with
package foo_testvs white-box withpackage foo - Test fixtures loaded from
testdata/ - Test naming convention:
TestFunc_Scenario_ExpectedResult - When to use
t.Fatalvst.Error
go-tests-tabledriven/
├── calc/
│ ├── calc.go # Add and Subtract functions
│ └── calc_test.go # Table-driven tests with parallel subtests and helpers
└── parser/
├── parser.go # SumFromJSON — parses a JSON array and returns the sum
├── parser_test.go # Tests using testdata fixtures
└── testdata/
└── numbers.json
# Run all tests
go test ./...
# Run with verbose output
go test -v ./...
# Run a specific subtest
go test -v -run TestAdd/both_negative ./calc/
# Run without cache
go test -count=1 ./...Each test case is a struct in a slice. A single loop iterates over all cases, calling t.Run for each one. This avoids code duplication and allows individual cases to be targeted with -run.
Mark a test helper function with t.Helper() so that when it fails, the error points to the caller line — not the line inside the helper.
Register a cleanup function that runs after the test finishes, regardless of pass, fail, or panic. More reliable than defer for setup/teardown in helpers.
Files in testdata/ directories are ignored by the Go compiler and are only accessible to tests. Use filepath.Join("testdata", "file.json") to reference them portably.