Go parallel test — capture range variable in test table
Problem
First of all, let’s take a look on a pretty simple test of a sum as below. The test just has two test cases, one expects a negative result and another one expects a positive result. However, there is a mistake in the first test case (“Must be negative”), the expected got value must be -5 instead 5.
package main
import (
"testing"
)
func TestParallelismWithTestTable(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
v int
go int
}{
{
name: "Must be negative",
v: -10,
got: 5,
},
{
name: "Must be positive",
v: 5,
got: 10,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
result := 5 + tc.v
if tc.got != result {
t.Errorf("%d != %d", tc.got, result)
}
})
}
}
--- PASS: TestParallelismWithTestTable (0.00s)
--- PASS: TestParallelismWithTestTable/Must_be_negative (0.00s)
--- PASS: TestParallelismWithTestTable/Must_be_positive (0.00s)
PASS
Check it on playground: https://go.dev/play/p/mUmfoCBH8Ad
Unfortunately, the test result tells that both test case pass, all good in our code. This is a very dangerous mistake in Go test because the test gives us a fake result that our code is perfect but in fact there are bugs there.
Reason
Test runs are created in parallel and variable tc is already assigned to the last test case when test runs are executed. Therefore, both test runs are done on the last test case and they all pass of-course.
How to avoid the problem
Similar to the common problem of executing go routines with a range variable, the range variable tc must be captured.
package main
import (
"testing"
)
func TestParallelismWithTestTable(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
v int
got int
}{
{
name: "Must be negative",
v: -10,
got: 5,
},
{
name: "Must be positive",
v: 5,
got: 10,
},
}
for _, tc := range testCases {
// CAPTURE THE RANGE VARIABLE tc
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
result := 5 + tc.v
if tc.got != result {
t.Errorf("%d != %d", tc.got, result)
}
})
}
}
--- FAIL: TestParallelismWithTestTable (0.00s)
--- FAIL: TestParallelismWithTestTable/Must_be_negative (0.00s)
--- PASS: TestParallelismWithTestTable/Must_be_positive (0.00s)
FAIL
Check it on Go play ground https://go.dev/play/p/kDNi4ACyb7A
Now the test works as we expect.