| --- | |
| title: Testable Examples in Go | |
| date: 2015-05-07 | |
| by: | |
| - Andrew Gerrand | |
| tags: | |
| - godoc | |
| - testing | |
| summary: How to add examples, which double as tests, to your packages. | |
| template: true | |
| --- | |
| ## Introduction | |
| Godoc [examples](/pkg/testing/#hdr-Examples) are snippets of | |
| Go code that are displayed as package documentation and that are verified by | |
| running them as tests. | |
| They can also be run by a user visiting the godoc web page for the package | |
| and clicking the associated "Run" button. | |
| Having executable documentation for a package guarantees that the information | |
| will not go out of date as the API changes. | |
| The standard library includes many such examples | |
| (see the [`strings` package](/pkg/strings/#Contains), | |
| for instance). | |
| This article explains how to write your own example functions. | |
| ## Examples are tests | |
| Examples are compiled (and optionally executed) as part of a package's test | |
| suite. | |
| As with typical tests, examples are functions that reside in a package's | |
| `_test.go` files. | |
| Unlike normal test functions, though, example functions take no arguments | |
| and begin with the word `Example` instead of `Test`. | |
| The [`reverse` package](https://pkg.go.dev/golang.org/x/example/hello/reverse/) | |
| is part of the [Go example repository](https://cs.opensource.google/go/x/example). | |
| Here's an example that demonstrates its `String` function: | |
| ```go | |
| package reverse_test | |
| import ( | |
| "fmt" | |
| "golang.org/x/example/hello/reverse" | |
| ) | |
| func ExampleString() { | |
| fmt.Println(reverse.String("hello")) | |
| // Output: olleh | |
| } | |
| ``` | |
| This code might live in `example_test.go` in the `reverse` directory. | |
| The Go package documentation server _pkg.go.dev_ presents this | |
| example alongside the [`String` function's documentation](https://pkg.go.dev/golang.org/x/example/hello/reverse/#String): | |
| {{image "examples/pkgdoc.png" 517}} | |
| Running the package's test suite, we can see the example function is executed | |
| with no further arrangement from us: | |
| $ go test -v | |
| === RUN TestString | |
| --- PASS: TestString (0.00s) | |
| === RUN ExampleString | |
| --- PASS: ExampleString (0.00s) | |
| PASS | |
| ok golang.org/x/example/hello/reverse 0.209s | |
| ## Output comments | |
| What does it mean that the `ExampleString` function "passes"? | |
| As it executes the example, | |
| the testing framework captures data written to standard output | |
| and then compares the output against the example's "Output:" comment. | |
| The test passes if the test's output matches its output comment. | |
| To see a failing example we can change the output comment text to something | |
| obviously incorrect | |
| ```go | |
| func ExampleString() { | |
| fmt.Println(reverse.String("hello")) | |
| // Output: golly | |
| } | |
| ``` | |
| and run the tests again: | |
| $ go test | |
| --- FAIL: ExampleString (0.00s) | |
| got: | |
| olleh | |
| want: | |
| golly | |
| FAIL | |
| If we remove the output comment entirely | |
| ```go | |
| func ExampleString() { | |
| fmt.Println(reverse.String("hello")) | |
| } | |
| ``` | |
| then the example function is compiled but not executed: | |
| $ go test -v | |
| === RUN TestString | |
| --- PASS: TestString (0.00s) | |
| PASS | |
| ok golang.org/x/example/hello/reverse 0.110s | |
| Examples without output comments are useful for demonstrating code that cannot | |
| run as unit tests, such as that which accesses the network, | |
| while guaranteeing the example at least compiles. | |
| ## Example function names | |
| Godoc uses a naming convention to associate an example function with a | |
| package-level identifier. | |
| ```go | |
| func ExampleFoo() // documents the Foo function or type | |
| func ExampleBar_Qux() // documents the Qux method of type Bar | |
| func Example() // documents the package as a whole | |
| ``` | |
| Following this convention, godoc displays the `ExampleString` example | |
| alongside the documentation for the `String` function. | |
| Multiple examples can be provided for a given identifier by using a suffix | |
| beginning with an underscore followed by a lowercase letter. | |
| Each of these examples documents the `String` function: | |
| ```go | |
| func ExampleString() | |
| func ExampleString_second() | |
| func ExampleString_third() | |
| ``` | |
| ## Larger examples | |
| Sometimes we need more than just a function to write a good example. | |
| For instance, to demonstrate the [`sort` package](/pkg/sort/) | |
| we should show an implementation of `sort.Interface`. | |
| Since methods cannot be declared inside a function body, the example must | |
| include some context in addition to the example function. | |
| To achieve this we can use a "whole file example." | |
| A whole file example is a file that ends in `_test.go` and contains exactly one | |
| example function, no test or benchmark functions, and at least one other | |
| package-level declaration. | |
| When displaying such examples godoc will show the entire file. | |
| Here is a whole file example from the `sort` package: | |
| {{raw ` | |
| ~~~go | |
| package sort_test | |
| import ( | |
| "fmt" | |
| "sort" | |
| ) | |
| type Person struct { | |
| Name string | |
| Age int | |
| } | |
| func (p Person) String() string { | |
| return fmt.Sprintf("%s: %d", p.Name, p.Age) | |
| } | |
| // ByAge implements sort.Interface for []Person based on | |
| // the Age field. | |
| type ByAge []Person | |
| func (a ByAge) Len() int { return len(a) } | |
| func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] } | |
| func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age } | |
| func Example() { | |
| people := []Person{ | |
| {"Bob", 31}, | |
| {"John", 42}, | |
| {"Michael", 17}, | |
| {"Jenny", 26}, | |
| } | |
| fmt.Println(people) | |
| sort.Sort(ByAge(people)) | |
| fmt.Println(people) | |
| // Output: | |
| // [Bob: 31 John: 42 Michael: 17 Jenny: 26] | |
| // [Michael: 17 Jenny: 26 Bob: 31 John: 42] | |
| } | |
| ~~~ | |
| `}} | |
| A package can contain multiple whole file examples; one example per file. | |
| Take a look at the [`sort` package's source code](/src/sort/) | |
| to see this in practice. | |
| ## Conclusion | |
| Godoc examples are a great way to write and maintain code as documentation. | |
| They also present editable, working, runnable examples your users can build on. | |
| Use them! |