Skip to content
gohacks.dev
Testing

Documentation That Runs as Tests in Go Packages Itself

Put a runnable Example in a Go package so the package page shows working code and go test keeps it correct: the package-level Example and function examples.

3 min read
Summarize in AI
Flat illustration of a test report with one failing test.

A README with a code sample is a promise that stops being true. The sample was right the day it was written, then the function gained a parameter, and the README now teaches code that does not compile. Go has a built-in answer that costs a few lines: examples in the test files, which run with go test.

Tested with Go 1.25.6, macOS on Apple Silicon (arm64)

temp.goGo
// Package temp converts temperatures.
package temp

// CToF converts Celsius to Fahrenheit.
func CToF(c float64) float64 { return c*9/5 + 32 }
temp_test.goGo
package temp_test

import (
	"fmt"

	temp "example.test/lab"
)

// Example shows the package in one line; go doc puts it under the package comment.
func Example() {
	fmt.Println(temp.CToF(100))
	// Output: 212
}

func ExampleCToF() {
	fmt.Printf("%.1f\n", temp.CToF(36.6))
	// Output: 97.9
}
Shell
go test -count=1 -v . 2>&1 | grep -E '^(--- |ok)' | sed -E 's/ \([0-9.]+s\)//; s/\t[0-9.]+s$//'
Text
--- PASS: Example
--- PASS: ExampleCToF
ok  	example.test/lab

Two kinds, one convention

func Example() with no suffix is the package example. On the package page, it appears under the package overview, which is where a new user looks first, so it should be the shortest working use of the package: construct, call, print. func ExampleCToF() is attached to the function of that name and appears under its documentation. Methods use ExampleType_Method, and a second example of the same thing adds a lower case suffix, as in ExampleCToF_negative. The full set of naming rules and the unordered and no-output variants are in the example tests article.

The recorded run shows both passing. CToF(100) prints 212. For 36.6 the example uses Printf("%.1f") so that floating point formatting does not make the comment depend on the last digit, printing 97.9. Pick an output that is stable: round, sort, or format.

What you get

If someone later changes CToF to return an int, or fixes a rounding bug and the output changes, the example fails and the author sees it before the change merges. The page on pkg.go.dev shows the same code with a Run button and the output from the comment. The go doc command in a terminal prints the documentation comments but not the examples, so look at the web page or a local pkgsite to see them.

Write the examples in the external test package (temp_test) so they use the API as a caller does. Keep them free of testing.T. Examples are documentation first, and a reader who sees t.Helper() in one wonders what it is for.

Keeping them honest

Three habits keep examples useful. Write the package example first, when you write the package, because explaining the first call is the best test of whether the API is shaped well; if the example needs fifteen lines of setup, that is feedback. Run go vet ./... in CI, which flags an example named for an identifier that does not exist, so a renamed function cannot leave an orphan behind. And review the output comment like any other assertion: it is the line that makes the example a test, and an example whose comment was edited to match a regression is a test that has been disabled.

Examples are compiled on every go test, including builds that do not run them, so an example with no output comment still catches an API change that breaks it. That is a cheap guard for snippets that touch the network or the clock.

For the Go team's description of the convention and its rendering, see the Go blog article on examples and the testing package documentation. Examples sit next to the other kinds of test in the testing guide.

Taggedquick tip
Share

Keep reading

Get new articles by email

We send one email when a new article is published. No spam.

Unsubscribe in one click. No spam, ever.