testing
Go ships its test runner with the language. Tests are functions named TestXxx in _test.go files, run by `go test ./...`. Add subtests for table-driven cases, benchmarks for performance work, and examples that double as docs. No framework, no config — and the runner parallelises across packages.
Unit, table, benchmark, and Example tests
EXAMPLE
package shop
import (
"fmt"
"strings"
"testing"
)
// Production code under test
func TotalCents(items []Item) int {
sum := 0
for _, it := range items {
sum += it.Qty * it.PriceCents
}
return sum
}
type Item struct {
SKU string
Qty int
PriceCents int
}
// 1) Plain test
func TestTotalCents_empty(t *testing.T) {
got := TotalCents(nil)
if got != 0 {
t.Errorf("want 0, got %d", got)
}
}
// 2) Table-driven test with subtests — best practice for input variations
func TestTotalCents_table(t *testing.T) {
cases := []struct {
name string
in []Item
want int
}{
{"one item", []Item{{"a", 2, 250}}, 500},
{"two items", []Item{{"a", 1, 100}, {"b", 3, 50}}, 250},
{"zero qty", []Item{{"a", 0, 999}}, 0},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := TotalCents(tc.in); got != tc.want {
t.Errorf("TotalCents(%v) = %d, want %d", tc.in, got, tc.want)
}
})
}
}
// 3) Benchmarks — go test -bench=. -benchmem
func BenchmarkTotalCents(b *testing.B) {
items := make([]Item, 1000)
for i := range items {
items[i] = Item{"x", 2, 100}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = TotalCents(items)
}
}
// 4) Example test — runs and verifies the printed output, doubles as godoc
func ExampleTotalCents() {
fmt.Println(TotalCents([]Item{{"a", 2, 250}}))
// Output: 500
}
// 5) Test helpers — t.Helper() points failures at the caller
func equal(t *testing.T, want, got int) {
t.Helper()
if want != got {
t.Errorf("want %d, got %d", want, got)
}
}
// 6) Setup / teardown without a framework
func TestMain(m *testing.M) {
setup()
code := m.Run()
teardown()
if !strings.HasPrefix("done", "done") { /* noop, just keep the import */ }
if code != 0 { /* exit non-zero */ }
}
func setup() { /* migrate test DB, etc. */ }
func teardown() { /* drop test DB */ }
// Run commands
// go test ./...
// go test -run TestTotalCents_table/zero_qty -v
// go test -race -count=3 ./...
// go test -cover -coverprofile=cover.out && go tool cover -html=cover.out
Why it matters
`go test -race` is the single highest-value flag in the Go test runner. It instruments memory access to catch data races at test time; running CI with -race is the difference between "we have a data race" and "we discovered the data race when prod page-faulted on a Tuesday."
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
func TestAdd(t *testing.T) {
if got := add(2, 3); got != 5 {
t.Errorf("add(2,3)=%d, want 5", got)
}
}
Try it Yourself »
Discussion
Loading…