GO-001 Prefer Table-Driven Tests
Express related test-cases as data to keep tests readable and exhaustive, and compare the results using full-structure comparison.
Table-driven tests express each case as a row of data, keeping intent obvious and making it cheap to add cases.
Why this matters
Table-driven tests improve code maintainability and test coverage by separating test logic from test data. This approach makes it trivial to add new test cases; simply add a row to the data structure without duplicating test logic. It also enhances readability by presenting all test scenarios in a clear, tabular format, making it easier to spot missing cases or inconsistencies.
Additionally, table-driven tests scale better as your test suite grows, reduce oilerplate code, and make failures more descriptive by automatically including the test case name in output.
Example
func TestAbs(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
in int
want int
}{
{
name: "Positive",
in: 3,
want: 3,
}, {
name: "Negative",
in: -3,
want: 3,
}, {
name: "Zero",
in: 0,
want: 0,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Act
v := math.Abs(tc.in)
// Assert
if got, want := v, tc.want; got != want {
t.Errorf("Abs(%d) = %d, want %d", tc.in, got, want)
}
})
}
}