go test, list & generate
go test runs package tests, go list introspects what the build system sees, and go generate triggers code generation hooks you define beside source files.
Summary
Testing is first-class in the toolchain: no external runner is required for unit tests, benchmarks, examples, or fuzz targets.
go list exposes packages, dependencies, tags, and module versions as machine-readable JSON for scripts and CI.
go generate runs //go:generate directives, usually shelling out to tools installed with go install.
Together they form the daily loop: list what exists, generate what is stale, test what changed.
Recipe
Quick-reference recipe card - copy-paste ready.
# Run all tests with race detector and coverage
go test -race -cover ./...
# Run one test by name with verbose output
go test -run TestParseConfig -v ./internal/config
# List packages as JSON for scripting
go list -json ./...
# Run all generators in the module
go generate ./...When to reach for this:
- Use
go test ./...as the default CI gate before merge. - Use
go list -ftemplates to build SBOM or ownership reports. - Use
go generateafter changing protobuf, stringer, or mock definitions. - Use
go test -benchwhen optimizing hot paths with reproducible numbers.
Working Example
// mathutil/sum.go
package mathutil
func Sum(nums []int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}// mathutil/sum_test.go
package mathutil
import "testing"
func TestSum(t *testing.T) {
got := Sum([]int{1, 2, 3})
if got != 6 {
t.Fatalf("got %d want 6", got)
}
}
func BenchmarkSum(b *testing.B) {
data := make([]int, 1000)
for i := range data {
data[i] = i
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
Sum(data)
}
}// api/doc.go
package api
//go:generate go run golang.org/x/tools/cmd/stringer@latest -type=Kind
type Kind int
const (
KindHTTP Kind = iota
KindGRPC
)go test -cover ./mathutil
go test -bench=. -benchmem ./mathutil
go list -f '{{.ImportPath}} tags={{.BuildTags}}' ./...
go generate ./apiWhat this demonstrates:
- Table-free unit test with
t.Fatalffor fast failure. - Benchmark with
b.ResetTimer()excluding setup work. go list -fcustom template for inventory scripts.//go:generatecolocated with the type it maintains.
Deep Dive
How It Works
go testcompiles a test main that links_test.gofiles, then executes the binary and streams results.- Test cache keys include source hashes, flags, and Go version; passing results reuse unless
-count=1. go listloads packages without necessarily linking;-jsonemits structs for automation.go generatescans for//go:generatelines and runs commands in file order; it does not track staleness itself.
go test Flags at a Glance
| Flag | Purpose |
|---|---|
-run regexp | Filter tests, benchmarks, fuzz by name |
-race | Enable race detector |
-cover | Print coverage percentage |
-coverprofile=file | Write coverage profile for go tool cover |
-bench regexp | Run benchmarks matching pattern |
-benchmem | Report allocations in benchmarks |
-timeout d | Kill tests after duration (default 10m) |
-count n | Run each test n times; 1 disables pass cache |
-shuffle on | Randomize test order to expose coupling |
go list Templates
| Template field | Meaning |
|---|---|
.ImportPath | Canonical import path |
.Dir | Directory on disk |
.GoFiles | Production .go files selected by tags |
.TestGoFiles | _test.go files in package |
.Deps | Transitive import paths |
.Module | Module metadata when in module mode |
go generate Rules
- Directives must start with
//go:generate(no space beforego). - Commands run with the package directory as working directory.
- Generators should be idempotent; commit outputs or enforce
git diffchecks in CI. - Prefer
go run tool@versionover undocumented local binaries for reproducibility.
Gotchas
- Assuming
go testwithout./...runs everything - bare.is only the current package. Fix:go test ./...from module root. - Flaky pass cached forever - default cache hides intermittent failures. Fix:
go test -count=1while debugging; fix root cause before merging. - Benchmarking without
-benchmem- allocation regressions stay invisible. Fix: add-benchmemand compare withbenchstat. go generatein CI without checking diffs - drift merges silently. Fix: run generate in a job that fails on non-emptygit diff.- External test package confusion -
package foo_testcannot access unexported symbols. Fix: test exported API or use internal test packagepackage foo. - List JSON parsing by hand - easy to miss embedded newlines. Fix: use
jqongo list -jsonoutput in scripts.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
gotestsum | JUnit XML and prettier CI logs | Standard go test -v is enough locally |
make generate | Team wants one entry for many generators | go generate ./... already covers the module |
buf generate / protoc direct | Protobuf pipelines with their own plugins | Simple stringer/mockgen hooks in Go files |
golangci-lint run | Lint beyond compiler and vet | You only need the stdlib test runner |
FAQs
What is the difference between Test and Benchmark functions?
func TestXxx(t *testing.T) asserts correctness.
func BenchmarkXxx(b *testing.B) measures performance and runs b.N iterations.
How do fuzz tests work?
func FuzzXxx(f *testing.F) seeds inputs then mutates them to find crashes.
Run with go test -fuzz=FuzzName (Go 1.18+).
Can go test run examples?
Yes.
func ExampleXxx() in _test.go files are compiled and optionally checked for stdout with // Output: comments.
Why does go list show different files than ls?
Build tags filter sources.
Files with //go:build ignore never appear in .GoFiles.
Does go generate run before every build?
No.
You invoke it explicitly or from CI/Makefile hooks.
How do I skip short tests in CI?
Mark long tests with if testing.Short() { t.Skip() } and run CI with -short.
What is test cache?
Passed test results stored under GOCACHE.
Changing sources or flags invalidates entries; failures are not cached.
Can I test main packages?
Yes, but package main tests often live in main_test.go beside main.go.
Consider extracting logic into importable packages for easier testing.
How do I print all module paths in the repo?
go list -m all from module root.
In workspace mode, run from the workspace root to include every module in go.work.
Should generators pin tool versions?
Yes.
Use go run pkg@version in directives so teammates and CI resolve the same tool.
Related
- The go Command: Build, Test, and Module Lifecycle - shared loader pipeline
- Go Toolchain Basics - first
go testandgo listexamples - go fix Modernizers & go vet Analyzers - static checks before tests
- Build Tags & Conditional Compilation - why list output differs by tags
- Go Toolchain Best Practices - CI testing habits
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).