Medium10 minGo Fundamentals
UpdatedAug 4, 2026
Edit

Table-Driven Tests

CONCEPTS:Go Table-Driven Testing

Question Variations

  • "What is a table-driven test in Go?"
  • "Why use `t.Run` inside a test loop?"
  • "How do you run Go tests with the race detector?"
  • "What is the difference between `t.Errorf` and `t.Fatalf`?"

Why This Is Asked

Go’s standard testing package encourages compact, readable tests. This question tests whether you can cover a behavior matrix without duplicated test code and produce failures that identify the case that broke.

Key Concepts

  • Test functions live in _test.go files and accept *testing.T.
  • A table captures named inputs and expected outcomes.
  • t.Run creates an isolated, named subtest for each case.
  • Tests should focus on observable behavior and include error paths and boundaries.

Question Variations

  • “What is a table-driven test in Go?”
  • “Why use t.Run inside a test loop?”
  • “How do you run Go tests with the race detector?”
  • “What is the difference between t.Errorf and t.Fatalf?”

Answers by Technology

+ Add Variant
GoImprove this answer ✏️

Expected Answer (Go 1.26.5)

A table-driven test represents cases as data and uses one shared test body to execute and assert them. Naming each case and wrapping it in t.Run gives clear failure output and allows cases to be selected individually. It is a convention, not a special language feature.

package word

import "testing"

func TestIsPalindrome(t *testing.T) {
	tests := []struct {
		name string
		input string
		want  bool
	}{
		{name: "palindrome", input: "level", want: true},
		{name: "ordinary word", input: "gopher", want: false},
		{name: "empty", input: "", want: true},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			if got := IsPalindrome(test.input); got != test.want {
				t.Errorf("IsPalindrome(%q) = %v, want %v", test.input, got, test.want)
			}
		})
	}
}

Use t.Errorf when a case can continue collecting assertions and t.Fatalf when the rest of that subtest cannot proceed. Run go test ./... for the package tree and add -race for concurrent code.

Why It Matters

Table-driven tests make boundary cases and error behavior visible without repeated setup. Clear case names shorten debugging time and make test suites easier to extend safely.

Common Mistakes

  • Using unnamed cases: A failure then gives little clue about the input that failed.
  • Sharing mutable setup between cases unintentionally: One case can affect another and make tests order-dependent.
  • Calling t.Fatal from a goroutine: Only the test goroutine should call fatal test methods; communicate errors back instead.

Follow-up Questions

  • What command runs all package tests? (Answer: go test ./....)
  • When should a test use t.Helper()? (Answer: In an assertion helper so failure locations point to its caller.)