testing.T.Run executes a named subtest with its own *testing.T value. Each subtest runs independently, so one fatal failure does not prevent other cases from running. section 2.5.1 section 2.5.3
Use Run inside a table-driven test to give every case a readable name and a closure containing its test logic. The closure can access the current case variables and call Fatalf or Errorf through its private *T. section 2.5.2 section 2.5.3
Table-driven subtests
func TestParseSubtests(t *testing.T) { for _, tt := range parseTests { t.Run(tt.name, func(t *testing.T) { got, err := Parse(tt.uri) if err != nil { t.Fatalf("Parse(%q) err = %v", tt.uri, err) } if *got != *tt.want { t.Errorf("got %#v, want %#v", got, tt.want) } }) } } Place shared setup and teardown around the loop when every subtest uses the same resources. section 2.5.2
The parent test waits for its subtests when they run sequentially, while failures remain isolated. Names also make reports readable and let go test select an individual subtest with a filter such as TestParseSubtests/full. section 2.5.4
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,
)
}
})
}
}
Inspect the complete table-driven subtest implementation, including named cases, closures, assertions, and shared setup placement. section 2.5.2
