go · level 3 · unit 7 · lesson 1
- meet it
- guess first
- your turn
- fix it
- nailed it
Tap a green word if you get lost.
meet it
A lists its cases as data, then loops.
A table-driven test lists its cases as data: a slice of small structs, each with a name, an input and the answer you want. The struct type is written in place, with no name of its own: an . One loop runs every case and compares what came out with what you wanted.
Adding a case is one more line, not one more function. Make failures quick to read: say which case failed, what went in, what came out and what was wanted, in the order got, then want.
In a real project the table lives in a file ending _test.go, inside func TestSlug(t *testing.T), where each case runs with t.Run and reports with t.Errorf. Here the same loop runs from main.
package main
import (
"fmt"
"strings"
)
func slug(title string) string {
return strings.ToLower(strings.Join(strings.Fields(title), "-"))
}
func main() {
tests := []struct {
name, in, want string
}{
{"spaces", "Hello World", "hello-world"},
{"extra spaces", " Go Level 3 ", "go-level-3"},
{"empty", "", ""},
}
for _, tt := range tests {
if got := slug(tt.in); got != tt.want {
fmt.Printf("FAIL %s: slug(%q) = %q, want %q\n", tt.name, tt.in, got, tt.want)
} else {
fmt.Println("PASS", tt.name)
}
}
} tt is the usual Go name for the current row of a test table.
guess first
The table catches something. What prints?
Guess before you run it. Wrong guesses are how you learn, and nothing is marked.
package main
import (
"fmt"
"strings"
)
func shout(s string) string {
return strings.ToUpper(s) + "!"
}
func main() {
tests := []struct {
name, in, want string
}{
{"word", "hi", "HI!"},
{"empty", "", ""},
}
for _, tt := range tests {
if got := shout(tt.in); got != tt.want {
fmt.Printf("FAIL %s: shout(%q) = %q, want %q\n", tt.name, tt.in, got, tt.want)
} else {
fmt.Println("PASS", tt.name)
}
}
}
your turn
Your turn. Add a row to the table.
Add a fourth case: a name for it, a year, and whether it is a leap year. A year that divides by 400, such as 2000, is a good one.
output
press run to see what happens
fix it
The table found a real bug. Can you fix it?
output
press run to see what happens
- Read the FAIL line: which year does
isLeapget wrong, and what should it say? - Centuries are not leap years, unless they divide by 400. The rule forgets that last part.
- Change the rule to
y%4 == 0 && (y%100 != 0 || y%400 == 0).
nailed it
your tests cover every case in one loop. :D
- List the cases as rows; one loop checks them all.
- Failure messages say the case, the input, got and want.
- In real projects the table lives in a
_test.gofile.
new words in your collection
- table-driven test
- anonymous struct
This lesson isn’t saved. A free account remembers every lesson you finish.