Go Mastery 5: Testing, Profiling and Internals
/ 2 min read
Table of Contents
1. Testing
-
*_test.go,TestXxx(t *testing.T) - Table-driven tests with
t.Runsubtests -
t.Helper(),t.Cleanup(),t.TempDir(),t.Setenv() -
t.Parallel() -
TestMain - Golden files
-
httptest.NewServer/httptest.NewRecorder - Mocking by interfaces (hand-written fakes beat generated mocks when possible)
-
testify(optional),go-cmpfor diffs - Coverage:
go test -coverprofile=c.out && go tool cover -html=c.out - Integration tests with testcontainers-go
-
testing/synctestfor concurrent code (newer Go)
Fuzzing
-
func FuzzXxx(f *testing.F),go test -fuzz=.
Benchmarks
-
func BenchmarkXxx(b *testing.B),b.Loop()/for i := 0; i < b.N; i++ -
-benchmem,-count,benchstatfor comparing runs - Avoid compiler optimizing away results
2. Profiling and performance
-
pprof: CPU, heap, goroutine, block, mutex profiles -
net/http/pprofin running services -
go tool pprof -http=:8080 cpu.prof -
go tool trace: execution tracer - Escape analysis:
go build -gcflags="-m" - Reduce allocations: preallocate slices (
make([]T, 0, n)), reuse buffers,sync.Pool,strings.Builder - Struct field ordering and padding
- Avoid premature optimization: measure first
- PGO (profile-guided optimization)
Visual: the performance workflow
flowchart TD
A["Write a benchmark"] --> B["Measure: go test -bench -benchmem"]
B --> C["Profile: pprof CPU and heap"]
C --> D{"Hotspot found?"}
D -- "yes" --> E["Change ONE thing"]
E --> F["Re-run benchmark, compare with benchstat"]
F --> D
D -- "no" --> G["Stop. Good enough."]
Rule: measure first, optimize second.
3. Runtime internals (for true mastery)
- Garbage collector: concurrent tri-color mark-and-sweep,
GOGC,GOMEMLIMIT - Goroutine stacks (growable)
- The scheduler: G, M, P, work stealing, preemption
- How maps, slices, strings, interfaces, and channels are laid out in memory
-
runtimepackage:NumGoroutine,ReadMemStats,GC - Read: A Guide to the Go Garbage Collector (go.dev/doc/gc-guide)
Practice
- Write benchmarks comparing
+=vsstrings.Buildervsbytes.Buffer - Take a slow function, profile it with pprof, and cut allocations by half
- Fuzz a parser and fix what it finds
- Reach 80%+ meaningful coverage on one of your projects
Previous: Go Mastery 4: Standard Library and Tooling
Next: Go Mastery 6: Backend and Production Go