目录

Go 其它测试

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)