diff options
author | Andrew Gerrand <adg@golang.org> | 2015-05-04 09:17:53 +1000 |
---|---|---|
committer | Andrew Gerrand <adg@golang.org> | 2015-05-07 00:21:55 +0000 |
commit | 65d9d2480437566f71df194ac4102901e1c4aa8c (patch) | |
tree | a8e60637c8a937b611379d95498cd0923b8e70cd | |
parent | eedd08fbad33909dcdc568a71fdb3cafd2bec27c (diff) |
blog: add Testable Examples article
Fixes golang/go#9471
Change-Id: I19b7b5bf62d6eee76e65730de535141638cf66e4
Reviewed-on: https://go-review.googlesource.com/9666
Reviewed-by: Rob Pike <r@golang.org>
-rw-r--r-- | content/examples.article | 201 | ||||
-rw-r--r-- | content/examples/reverse.png | bin | 0 -> 32227 bytes |
2 files changed, 201 insertions, 0 deletions
diff --git a/content/examples.article b/content/examples.article new file mode 100644 index 0000000..aa0dd28 --- /dev/null +++ b/content/examples.article @@ -0,0 +1,201 @@ +Testable Examples in Go +7 May 2015 +Tags: godoc, testing + +Andrew Gerrand + +* Introduction + +Godoc [[http://golang.org/pkg/testing/#hdr-Examples][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 [[http://golang.org/pkg/strings/#Contains][`strings` package]], +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 [[https://godoc.org/github.com/golang/example/stringutil/][`stringutil` package]] +is part of the [[https://github.com/golang/example][Go example repository]]. +Here's an example that demonstrates its `Reverse` function: + + package stringutil_test + + import ( + "fmt" + + "github.com/golang/example/stringutil" + ) + + func ExampleReverse() { + fmt.Println(stringutil.Reverse("hello")) + // Output: olleh + } + +This code might live in `example_test.go` in the `stringutil` directory. + +Godoc will present this example alongside the `Reverse` function's documentation: + +.image examples/reverse.png + +Running the package's test suite, we can see the example function is executed +with no further arrangement from us: + + $ go test -v + === RUN TestReverse + --- PASS: TestReverse (0.00s) + === RUN: ExampleReverse + --- PASS: ExampleReverse (0.00s) + PASS + ok github.com/golang/example/stringutil 0.009s + +* Output comments + +What does it mean that the `ExampleReverse` 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 + + func ExampleReverse() { + fmt.Println(stringutil.Reverse("hello")) + // Output: golly + } + +and run the tests again: + + $ go test + --- FAIL: ExampleReverse (0.00s) + got: + olleh + want: + golly + FAIL + +If we remove the output comment entirely + + func ExampleReverse() { + fmt.Println(stringutil.Reverse("hello")) + } + +then the example function is compiled but not executed: + + $ go test -v + === RUN TestReverse + --- PASS: TestReverse (0.00s) + PASS + ok github.com/golang/example/stringutil 0.009s + +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. + + 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 `ExampleReverse` example +alongside the documentation for the `Reverse` 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 `Reverse` function: + + func ExampleReverse() + func ExampleReverse_second() + func ExampleReverse_third() + + +* Larger examples + +Sometimes we need more than just a function to write a good example. + +For instance, to demonstrate the [[https://golang.org/pkg/sort/][`sort` package]] +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: + + 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 [[https://golang.org/src/sort/][`sort` package's source code]] +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! diff --git a/content/examples/reverse.png b/content/examples/reverse.png Binary files differnew file mode 100644 index 0000000..c9b5ad2 --- /dev/null +++ b/content/examples/reverse.png |