iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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…