CodeBlanks Logo Icon CodeBlanks

Table-driven tests

Learn the table-driven test pattern.

Table-driven tests

Go's most common test pattern: keep inputs and expected outputs in a slice and try them in one loop.

tests := []struct {
    ad   string
    a, b int
    want int
}{
    {"pozitif", 2, 3, 5},
    {"negatif", -2, -3, -5},
    {"sıfır", 0, 0, 0},
}
for _, tt := range tests {
    t.Run(tt.ad, func(t *testing.T) {
        if got := Topla(tt.a, tt.b); got != tt.want {
            t.Errorf("got %d, want %d", got, tt.want)
        }
    })
}

Adding a case is cheap

When a bug is reported, a new row that catches it is added to the table; the test code doesn't change. The table is a good checklist so edge cases (empty input, boundary values, Unicode) aren't forgotten. go test -run 'TestTopla/negatif' runs only that subtest.

Practice Table loop

Write Mutlak(n int) int. Loop over an anonymous struct table of the cases {-3, 3}, {0, 0}, {4, 4}; print how many cases pass.

Starter code

main.go
package main

import "fmt"

func main() {
    // buraya yaz
}

Sample solution

main.go
package main

import "fmt"

func Mutlak(n int) int {
    if n < 0 {
        return -n
    }
    return n
}

func main() {
    tests := []struct{ in, want int }{{-3, 3}, {0, 0}, {4, 4}}
    gecen := 0
    for _, tt := range tests {
        if Mutlak(tt.in) == tt.want {
            gecen++
        }
    }
    fmt.Println(gecen)
}

Expected output

3

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What is the advantage of using <code>t.Run</code> in a table test?

  • Tests compile faster
  • No failure message is needed
  • Coverage increases
  • Each case is reported as a separate subtest and can be run alone with -run Correct answer

Explanation: Subtest names appear in the output (TestTopla/negatif); even if one case calls Fatal, the others keep running.

2 Subtest

Question
for _, tt := range tests {
    t.____(tt.ad, func(t *testing.T) {
        // ...
    })
}
Answer
for _, tt := range tests {
    t.Run(tt.ad, func(t *testing.T) {
        // ...
    })
}

Explanation: t.Run starts a subtest with the given name.

3 Table part

Term Meaning
ad Subtest name
input fields What the function gets
want Expected result
t.Run Separate reporting

All lessons in Intermediate