Stage 1 · Code
Testing & Debugging in Go
Subtests, Coverage & Benchmarks
Organise tests into subtests, track which lines are covered, and measure performance with benchmarks.
Subtests with t.Run
t.Run(name, func) creates a named subtest under the parent. Subtests run independently — a failure in one does not stop the others. Run a single subtest with -run TestParent/sub_name.
# Run only the "negative" subtest
go test -run TestAdd/negative ./...
# Run all subtests matching "zero"
go test -run TestAdd/zero ./...Coverage Reports
Coverage measures what fraction of your code lines are executed by tests. The HTML report highlights untested lines in red — a fast way to find gaps.
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
# Print function-level coverage to the terminal
go tool cover -func=coverage.out | grep -v "100.0%"100% line coverage can coexist with untested error branches. Make sure your tests cover the happy path, error returns, edge cases (empty input, zero, nil), and concurrency races.
Benchmark Functions
Benchmark functions start with Bench and receive *testing.B. The loop for i := 0; i < b.N; i++ runs the benchmarked code as many times as Go needs for a stable measurement.
# Run all benchmarks (tests are skipped)
go test -bench=. ./...
# Run a specific benchmark
go test -bench=BenchmarkJoin ./...
# Benchmark with memory allocation statistics
go test -bench=. -benchmem ./...Test Helpers
Mark a function as a test helper with t.Helper(). Error messages from helpers report the caller's line, not the helper's line — making failures easier to trace.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.