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 hello
import (
"fmt"
"strings"
)
func ExampleToUpper() {
fmt.Println(strings.ToUpper("foo"))
// Output: FOO
}
func ExampleToUpperFail() {
fmt.Println(strings.ToUpper("bar"))
// Output: 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 hello
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 SortByAge []Person
func (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)
// Output:
// [Jack: 31 John: 42 Acme: 17 Pony: 26 Wise: 33]
// [Acme: 17 Pony: 26 Jack: 31 Wise: 33 John: 42]
}
|
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 hello
import (
"go/ast"
"go/parser"
"log"
)
func ExampleAST() {
expr, err := parser.ParseExpr("9 / (2 + 1)")
if err != nil {
log.Fatal(err)
}
ast.Print(nil, expr)
// Output: 0 *ast.BinaryExpr {
// 1 . X: *ast.BasicLit {
// 2 . . ValuePos: 1
// 3 . . Kind: INT
// 4 . . Value: "9"
// 5 . }
// 6 . OpPos: 3
// 7 . Op: /
// 8 . Y: *ast.ParenExpr {
// 9 . . Lparen: 5
// 10 . . X: *ast.BinaryExpr {
// 11 . . . X: *ast.BasicLit {
// 12 . . . . ValuePos: 6
// 13 . . . . Kind: INT
// 14 . . . . Value: "2"
// 15 . . . }
// 16 . . . OpPos: 8
// 17 . . . Op: +
// 18 . . . Y: *ast.BasicLit {
// 19 . . . . ValuePos: 10
// 20 . . . . Kind: INT
// 21 . . . . Value: "1"
// 22 . . . }
// 23 . . }
// 24 . . Rparen: 11
// 25 . }
// 26 }
}
|
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 hello
import (
"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)
|