Go Table-Driven Tests: Test More with Less Code
In Go, put inputs and expected results in a table and run each case through the same test logic. With Go 1.22+ language semantics, loop-declared variables are fresh per iteration; older parallel subtests may need an explicit per-case copy.
Why it exists
Writing a separate test function for every input and expected output is repetitive and hard to maintain. If you find yourself copy-pasting test code just to change the input values, you're creating technical debt. Table-driven tests were adopted as a Go idiom to solve this exact problem of repetition.
The mental model
Think of it like a spreadsheet for your tests. Each row is a complete test case with columns for "name", "input", and "expected output". A single piece of test logic then iterates through each row, runs the function with the given input, and checks if the actual result matches the expected one. You write the test logic once and amortize it across dozens of cases.
How it works
You start by defining a struct that represents a single test case, for example, struct { name string; input int; want int }. Then, you declare a slice of these structs, populating it with all your edge cases. Your main test function, TestMyFunction, will contain a single loop that iterates over this slice. Inside the loop, you use t.Run(testCase.name, ...) to create a distinct subtest for each case. This ensures that test failures clearly identify which case failed, making debugging much faster.
When to use it
This pattern is the default for testing functions in Go. It's especially powerful for functions with clear input-output behavior that need to be checked against many edge cases. Use it for testing mathematical functions, string manipulation, parsers, or any pure function. It makes adding new test cases trivial—you just add a new entry to the table.
When not to use it
While versatile, the pattern can become clumsy for tests requiring complex, unique setup or teardown for each case. If every test case needs a different mock object, a database connection in a specific state, or other complex environmental setup, writing separate test functions might be clearer than trying to cram that logic into the table structure.
One canonical example
A classic use case is testing a string formatting function. The fmt package itself uses this. You'd define a table where each entry has an input format string (like "%-1.2a") and its expected output ("[%-1.2a]"). The test loops through these, calls Sprintf on the input, and uses t.Errorf to report any mismatch with the expected output, like t.Errorf("got %q, want %q", got, tc.want).
Source: go.dev
Version note
With Go 1.22+ language semantics, variables declared by the loop have per-iteration scope. The older recommendation to copy the loop variable for every parallel subtest is not universally needed. Check the package language version and whether the loop declares a new variable or reuses an existing one.
Interview question
When is the Go table-driven test pattern most effectively applied?
- a.As a universal replacement for all other testing patterns in Go to reduce code.
- b.When each test case requires a significantly different mock object or database state.
- c.When testing asynchronous operations or concurrent code to prevent race conditions.
- d.For functions that have a clear input-output relationship and many distinct edge cases.Correct
Why? this is the answer
The card states table-driven tests are "especially powerful for functions with clear input-output behavior that need to be checked against many edge cases." Option B describes a scenario where the pattern is explicitly advised against due to complex setup requirements.
Just read this? Test yourself on what you have been reading.
Read the original → go.dev
Put your scrolling time to good use
Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Open roles that interview on go — each one lists the topics its interview covers.
See open roles