Go 的 testing 包除了测试,还提供了运行并验证示例的功能。一方面是文档的效果,是关于某个功能的使用例子;另一方面,可以被当做测试运行。
示例测试 示例测试函数格式 Go 语言通过大量的命名约定来简化工具的复杂度,规范代码的风格。对示例函数的命名有如下约定:
包级别的示例函数,直接命名为:func Example() { ... }
; 函数 F
的示例,命名为:func ExampleF() { ... }
; 类型 T
的示例,命名为:func ExampleT() { ... }
; 类型 T
上的方法 M
的示例,命名为:func ExampleT_M() { ... }
; 有时,我们想要给 包/类型/函数/方法
提供多个示例,可以通过在示例函数名称后附加一个不同的后缀来实现,但这种后缀必须以小写字母开头,如:
1 2 3 4 func Example_suffix () { ... }func ExampleF_suffix () { ... }func ExampleT_suffix () { ... }func ExampleT_M_suffix () { ... }
示例测试实例 示例一 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package helloimport ( "fmt" "strings" ) func ExampleToUpper () { fmt.Println(strings.ToUpper("foo" )) } func ExampleToUpperFail () { fmt.Println(strings.ToUpper("bar" )) }
1 2 3 4 5 6 7 8 9 10 11 12 $ go test -v -timeout 30s -run '^(ExampleToUpper|ExampleToUpperFail)$' example.com/hello === RUN ExampleToUpper --- PASS: ExampleToUpper (0.00s) === RUN ExampleToUpperFail --- FAIL: ExampleToUpperFail (0.00s) got: BAR want: Bar FAIL FAIL example.com/hello 0.008s
示例二 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package helloimport ( "fmt" "sort" ) type Person struct { Name string Age int } func (p Person) String () string { return fmt.Sprintf("%s: %d" , p.Name, p.Age) } type SortByAge []Personfunc (a SortByAge) Len () int { return len (a) }func (a SortByAge) Swap (i, j int ) { a[i], a[j] = a[j], a[i] }func (a SortByAge) Less (i, j int ) bool { return a[i].Age < a[j].Age }func Example () { people := []Person{ {"Jack" , 31 }, {"John" , 42 }, {"Acme" , 17 }, {"Pony" , 26 }, {"Wise" , 33 }, } fmt.Println(people) sort.Sort(SortByAge(people)) fmt.Println(people) }
1 2 3 4 5 6 $ go test -v -timeout 30s example.com/hello === RUN Example --- PASS: Example (0.00s) PASS ok example.com/hello 0.007s
实例三 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package helloimport ( "go/ast" "go/parser" "log" ) func ExampleAST () { expr, err := parser.ParseExpr("9 / (2 + 1)" ) if err != nil { log.Fatal(err) } ast.Print(nil , expr) }
1 2 3 4 5 6 $ go test -v -timeout 30s -run '^ExampleAST$' example.com/hello === RUN ExampleAST --- PASS: ExampleAST (0.00s) PASS ok example.com/hello (cached)
主函数测试 注意:在 TestMain
函数的最后,应该使用 m.Run
的返回值作为参数去调用 os.Exit
。
在写测试时,有时需要在测试之前或之后进行额外的设置(setup)或拆卸(teardown);有时,测试还需要控制在主线程上运行的代码。为了支持这些需求,testing
包提供了 TestMain
函数(附上 官方 TestMain 使用姿势 ):
1 func TestMain (m *testing.M)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package helloimport ( "log" "os" "testing" ) func TestMain (m *testing.M) { log.Println("Do stuff BEFORE the tests!" ) exit := m.Run() log.Println("Do stuff AFTER the tests!" ) os.Exit(exit) } func TestA (t *testing.T) { log.Println("TestA running" ) } func TestB (t *testing.T) { log.Println("TestB running" ) }
1 2 3 4 5 6 7 8 9 10 11 12 $ go test -v -timeout 30s -run '^(TestMain|TestA|TestB)$' example.com/hello 2021/07/22 20:07:25 Do stuff BEFORE the tests! === RUN TestA 2021/07/22 20:07:25 TestA running --- PASS: TestA (0.00s) === RUN TestB 2021/07/22 20:07:25 TestB running --- PASS: TestB (0.00s) PASS 2021/07/22 20:07:25 Do stuff AFTER the tests! ok example.com/hello (cached)