A subtest is a named test function run under a parent test. In Go, call t.Run with a name and a function containing one case’s assertions. Each subtest receives its own *testing.T, so its Errorf or Fatalf reports failure for that subtest rather than directly stopping its siblings. section 2.5.1 section 2.5.3
Subtests are especially useful for table-driven tests. A plain table loop puts every case in one test function: a Fatalf in an early case stops the function and skips later cases. Wrapping each row in t.Run keeps the shared test logic while isolating cases, producing a fuller test report after failures. section 2.4.4 section 2.5.2
Listing 2.10 Table-driven subtests (url/url_test.go)
var parseTests = []struct {
. . .
}{
{
name: "with_data_scheme",
. . .
},
{
name: "full",
. . .
},
{
name: "without_path",
. . .
},
}
func TestParseSubtests(t *testing.T) {
// We can put common test setup and teardown logic here.
for _, tt := range parseTests {
t.Run(tt.name, func(t *testing.T) { #1
got, err := Parse(tt.uri)
if err != nil {
t.Fatalf(
"Parse(%q) err = %v, want <nil>",
tt.uri, err,
)
}
if *got != *tt.want {
t.Errorf(
"Parse(%q)\ngot %#v\nwant %#v",
tt.uri, got, tt.want,
)
}
})
}
}
This pattern iterates over named URL cases and invokes t.Run once per row. The closure performs the same Parse checks as a conventional table-driven test, while the parent remains the place for setup and teardown shared by all cases. section 2.5.2
Use subtests to make a test suite easier to inspect and control:
- Give each case a unique, descriptive name. Names appear beneath the parent in verbose test output, making each scenario’s purpose and status visible. section 2.5.2 section 2.5.4
- Select one nested case with go test -run using its parent/subtest name, such as TestParseSubtests/full. The parent invokes Run for all cases, but the test filter skips nonmatching subtests. section 2.5.4
- Put common setup and teardown in the parent when sibling cases can share it, for example opening and closing a database connection once around the subtests. section 2.5.2
- Keep each *testing.T with its own test function. Sharing a T between tests or subtests can make results unpredictable and compromise their validity. section 2.5.3
Isolating URL parser cases
A parser test table includes with_data_scheme, full, and without_path cases. If the data-scheme case calls Fatalf in one ordinary table-driven test, full and without_path never run. As subtests, the data-scheme case can fail while the other two still run and report their own results. After Parse is fixed, the same named cases all pass. section 2.4.4 section 2.5.4 section 2.5.5
A parent normally runs its subtests sequentially and waits for each to finish. Subtests can also run in parallel, but both the parent and each participating subtest must be marked parallel; otherwise parallel subtests wait until the parent completes. Use this when independent cases benefit from concurrent execution. section 2.5.4 section 3.4.2
Subtests are hierarchical: a subtest can run another subtest. Top-level tests are also run internally through Run, so subtests extend the testing package’s existing execution model rather than forming a separate kind of test. section 2.5.5
