Testing Basics
Go has built-in testing — no frameworks needed. Create a file ending in _test.go and write functions starting with Test:
// calc.go
package calc
func Add(a, b int) int { return a + b }
// calc_test.go
package calc
import "testing"
func TestAdd(t *testing.T) {
got := Add(2, 3)
want := 5
if got != want {
t.Errorf("Add(2, 3) = %d, want %d", got, want)
}
}
go test -v ./...
Output
=== RUN TestAdd --- PASS: TestAdd (0.00s) PASS
Table-Driven Tests (Go Idiom)
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive", 2, 3, 5},
{"negative", -1, 1, 0},
{"zeros", 0, 0, 0},
{"large", 1000000, 1, 1000001},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Add(tt.a, tt.b)
if got != tt.expected {
t.Errorf("Add(%d, %d) = %d, want %d", tt.a, tt.b, got, tt.expected)
}
})
}
}
Key Takeaway: Table-driven tests are the Go idiom. They're easy to read, easy to extend, and each case runs as a subtest with its own name. Always use this pattern.
Benchmarks
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(2, 3)
}
}
go test -bench=. -benchmem
Practice Exercises
Medium Build a Mini Project
Combine concepts from this tutorial to build a small utility or tool.
Medium Debug Challenge
Introduce a bug in one of the code examples and practice finding and fixing it.
Hard Refactoring Exercise
Rewrite one example using a different approach and compare the tradeoffs.