Go has one of the best built-in testing stories of any language. No framework to install, no configuration files, no test runner debates. Just go test ./... and you are done.
This guide covers everything from writing your first test to running a full integration suite in CI.
Part 1: The Basics
Every test file ends in _test.go. Every test function starts with Test and takes a *testing.T:
func TestAdd(t *testing.T) {
got := Add(2, 3)
if got != 5 {
t.Errorf("Add(2, 3) = %d, want 5", got)
}
}
Run it:
go test ./...
Part 2: Table-Driven Tests
The standard Go pattern for testing multiple cases:
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
want int
}{
{"positive", 2, 3, 5},
{"zero", 0, 0, 0},
{"negative", -1, 1, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := Add(tt.a, tt.b); got != tt.want {
t.Errorf("Add(%d, %d) = %d, want %d", tt.a, tt.b, got, tt.want)
}
})
}
}
Part 3: Integration Tests with Build Tags
Separate slow integration tests from fast unit tests using build tags:
//go:build integration
package db_test
func TestDatabaseQuery(t *testing.T) {
// requires a running database
}
Run only unit tests: go test ./...
Run everything: go test -tags=integration ./...
Part 4: CI Pipeline
Coming soon — GitHub Actions workflow, test caching, and coverage reporting.