原文: https://blog.golang.org/examples
简介
Go自动生成的文档中有时你会看到代码例子,这些例子还可以通过点击网站上的Run按钮运行。而这些例子是从测试代码中提取出来的。
这些代码例子是具有可测性的,而带来的优点就是当API变化的时候,代码能够及时的同步。标准库中有很多库都包含代码例子,比如strings package
这篇文章将会教会你如何给一个库写例子代码。
例子代码
例子代码都是写在库的测试用例集里面的(以_test.go
结尾的文件)。跟普通的测试函数不同的是,例子代码的函数不接受任何参数,命令函数命名以Example
开头(普通测试函数是以Test
开头的)
Go的官方例子代码库里可以找到一个 stringutil库,这个库实现了Reverse
函数
package stringutil_test
import (
"fmt"
"github.com/golang/example/stringutil"
)
func ExampleReverse() {
fmt.Println(stringutil.Reverse("hello"))
// Output: olleh
}
上面这段代码应该可以在example_test.go
里面找到。这段例子代码会自动在godoc中的Reverse函数的说明下找到。
运行该库的测试用例,我们可以看到相关的Example函数也跟着一起运行了。
$ go test -v
=== RUN TestReverse
--- PASS: TestReverse (0.00s)
=== RUN: ExampleReverse
--- PASS: ExampleReverse (0.00s)
PASS
ok github.com/golang/example/stringutil 0.009s
输出的内容
程序是如何确定Example函数是否PASS呢?
当运行Example的测试用例的时候,测试框架捕获了其标准输出,并且用其跟注释中`Output: 开头的内容进行的对比,来确定是否调用成功
简单的修改下注释,就可以让这个测试用例FAIL了
func ExampleReverse() {
fmt.Println(stringutil.Reverse("hello"))
// Output: golly
}
重新运行后的效果
$ go test
--- FAIL: ExampleReverse (0.00s)
got:
olleh
want:
golly
FAIL
如果我们把Example里面的注释都删掉呢
func ExampleReverse() {
fmt.Println(stringutil.Reverse("hello"))
}
所有Example开头的函数虽然会写到文档中,但是不会作为测试程序执行了
$ go test -v
=== RUN TestReverse
--- PASS: TestReverse (0.00s)
PASS
ok github.com/golang/example/stringutil 0.009s
这种情况适用于依赖外部环境,无法作为单侧用例执行的,比如依赖一个外部的网络服务。
例子函数名
Godoc是通过名字来将其关联到相应的函数的
func ExampleFoo() // 作为Foo函数或者类型例子
func ExampleBar_Qux() // 作为Bar类型的Qux函数例子
func Example() // 作为整个库的例子
通过这种规则,godoc将ExampleReverse
函数对应到了Reverse
这个函数
通过增加 下划线+小写字母开头的单词作为后缀,一个函数可以对应多个例子。例如
func ExampleReverse()
func ExampleReverse_second()
func ExampleReverse_third()
这3个函数对对应于Reverse
这个函数。
更完整的例子
有些时候一个函数的例子已经无法说明一个函数该如何使用。
TODO 待翻译(困死了,先睡会)
For instance, to demonstrate the 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 sort package's source code to see this in practice.
结论
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!
By Andrew Gerrand