Table Driven Tests
Fast recall
Quick revision
Core answer
Whether you understand Table Driven Tests beyond memorized syntax.
Giving a definition without explaining a practical trade-off.
Connect the concept to a small production example and state its complexity or failure mode.
Interview-ready guide
Interview answer
The pattern, executed:
func TestDouble(t *testing.T) {
tests := []struct {
name string
in int
want int
}{
{"positive", 5, 10},
{"zero", 0, 0},
{"negative", -3, -6},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if double(tc.in) != tc.want {
t.Errorf("double(%d) = %d, want %d", tc.in, double(tc.in), tc.want)
}
})
}
}Executed output: each subtest named and passed:
=== RUN TestDouble/positive
=== RUN TestDouble/zero
=== RUN TestDouble/negative
--- PASS: TestDouble (with subtests)The advantages that made it THE Go pattern:
- 1Add a case = add one struct literal — no copy-paste of test functions
- 2t.Run creates SUBTESTS — each case reports separately; the failure tells you WHICH case (TestDouble/negative FAILED)
- 3Run a single case: go test -run TestDouble/negative
- 4The test data and the assertion logic are SEPARATE — the table is the specification
The conventions: a name field for readability; the anonymous struct in the function (or a named one for reuse); t.Run always (not a plain for loop — the subtest isolation is the point).
Table Driven Tests
Direct Answer
A slice of test cases (input, expected) iterated with t.Run() — each case is a named subtest with its own PASS/FAIL. The standard because: one loop tests many cases, failures name the case, and adding a case is one line.
Deep Dive & Architecture
The anonymous-struct idiom: the test-case struct is declared inline — it exists only for this table, and its fields document what the test data IS. For shared tables across tests, promote it to a named type.
The t.Run mechanics: each call creates a subtest with its own *testing.T — failures in one subtest don't stop the others (unless t.Fatal inside). The names form a path (TestDouble/negative) — the -run flag matches it (a regex over the full name).
The cleanup composable: t.Cleanup inside a t.Run registers per-subtest cleanup — each subtest gets its own setup/teardown through the same t that carries its lifetime. For shared setup, TestMain or t.Helper-wrapped init functions run once per package/test.
Common Mistakes & Pitfalls
Giving a definition without explaining a practical trade-off.
Check your understanding
Answer these without looking back at the article.
- When would you use Table Driven Tests in a real project?
- How would you compare Table Driven Tests with an alternative?
- What is a common mistake when using Table Driven Tests?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.