In Go, it is very common to use table-driven tests:
tests := struct {
name string
input T
...
} {{
name: "Foo",
...,
}, {
name: "Bar",
...,
}
... // maybe dozens or hundreds more cases
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := fizz(tt.input)
if err != nil {
t.Fatalf("fizz error: %v", err) // my_test.go:1234
}
})
}
When this test fails, it prints with something like:
--- FAIL: Test/Bar (0.00s)
my_test.go:1234: fizz error: the world blew up
- The
my_test.go:1234 tells us where in the test logic this failed.
- The
Test/Bar name tells us which test case failed.
Most code editors today identify source.go:1234 strings and automatically provide the ability to jump to that source code location. This is really helpful for jumping to the execution logic that failed, but is not helpful for jumping to the test data that caused the failure. It is impossible for editor tooling to automatically correlate the the test name (e.g., Test/Bar) with the test case in the code since the association between the two can be determined by arbitrary Turing-complete logic.
I propose the following API in the testing package:
// NameFileLine is a name combined with a file and line number.
type NameFileLine struct { ... }
// Name constructs a NameFileLine.
// It annotates the name with the file and line number of the caller.
func Name(name string) NameFileLine
// RunName runs f as a subtest of t called name.
func (t *T) RunName(name NameFileLine, f func(t *testing.T))
// RunName runs f as a subtest of b called name.
func (b *B) RunName(name NameFileLine, f func(b *testing.B))
Using this API, the example above would be changed as:
tests := struct {
- name string
+ name testing.NameFileLine
input T
...
} {{
- name: "Foo",
+ name: testing.Name("Foo"),
...,
}, {
- name: "Bar",
+ name: testing.Name("Bar"), // my_test.go:321
...,
}
... // maybe dozens or hundreds more cases
}
for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
+ t.RunName(tt.name, func(t *testing.T) {
got, err := fizz(tt.input)
if err != nil {
t.Fatalf("fizz error: %v", err) // my_test.go:1234
}
})
}
- We call
testing.Name in every test case, which captures file and line information about where the test case was declared.
- We call
testing.T.RunName and pass it the testing.TestName so that the subtest knows what test case is associated with this subtest.
Thus, the test output would be something like:
--- FAIL: Test/Bar (0.00s)
my_test.go:321: my_test.go:1234: fizz error: the world blew up
- The
my_test.go:321 tells us where the test data was declared.
- The
my_test.go:1234 tells us where in the test logic this failed.
Now, we can click on my_test.go:321 in our code editors and it will take us directly to the test case declaration.
In Go, it is very common to use table-driven tests:
When this test fails, it prints with something like:
my_test.go:1234tells us where in the test logic this failed.Test/Barname tells us which test case failed.Most code editors today identify
source.go:1234strings and automatically provide the ability to jump to that source code location. This is really helpful for jumping to the execution logic that failed, but is not helpful for jumping to the test data that caused the failure. It is impossible for editor tooling to automatically correlate the the test name (e.g.,Test/Bar) with the test case in the code since the association between the two can be determined by arbitrary Turing-complete logic.I propose the following API in the
testingpackage:Using this API, the example above would be changed as:
tests := struct { - name string + name testing.NameFileLine input T ... } {{ - name: "Foo", + name: testing.Name("Foo"), ..., }, { - name: "Bar", + name: testing.Name("Bar"), // my_test.go:321 ..., } ... // maybe dozens or hundreds more cases } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { + t.RunName(tt.name, func(t *testing.T) { got, err := fizz(tt.input) if err != nil { t.Fatalf("fizz error: %v", err) // my_test.go:1234 } }) }testing.Namein every test case, which captures file and line information about where the test case was declared.testing.T.RunNameand pass it thetesting.TestNameso that the subtest knows what test case is associated with this subtest.Thus, the test output would be something like:
my_test.go:321tells us where the test data was declared.my_test.go:1234tells us where in the test logic this failed.Now, we can click on
my_test.go:321in our code editors and it will take us directly to the test case declaration.