测试库

Go 内置的测试库:

  • testing:最基本的测试库。
  • net/http/httptest:基于 tesing 实现的专用于 Web 应用测试。

其他第三方测试库:

着重了解 testing 库。

testing 库介绍

testing 库有两个重要的数据结构:testing.T, tesing.B,可以进行三种测试:单元测试,基准测试和示例测试。

命名规范

若要测试某 Go 源文件,则测试文件要求以该文件名开头,以 _test.go 结尾,且两者在同一包内。

  • 如 server.go 的测试文件应该命名为 server_test.go

若要测试某函数,测试函数的形式要求(注意严格遵循大小写。):

  • 单元测试:TestXxx
  • 基准测试:BenchmarkXxx
  • 示例函数:ExampleXxx

    测试命令

    go test 命令会执行对当前目录下所有以 _test.go 为后缀的文件进行测试,测试流程:

  • 生成一个临时的 main 包来调用所有的测试函数

  • 编译、运行、汇报结果
  • 清空临时文件

单元测试

单元测试是进行功能测试,使用 testing.T 结构。
一个单元是程序的一个模块,通常是一个函数(通常的意思是有可能不是),程序中的一个部分能否独立地进行测试,是评判这个部分能否被归纳为单元的一个重要指标。
常用标志:

  • -v 显示更详细的信息。
  • -cover 显示覆盖率。
  • -short:跳过长时间的测试。
  • -run:后面接正则表达式,执行符合条件的测试函数。
  • -parallel:并行测试。当单元之间没有依赖关系时可以进行并行测试,-parallel 10 表示最多并行测试 10 个单元。
  • -benchmem:显示内存信息。

常用方法:

  1. // 记录日志
  2. Log(args... interface{})
  3. Logf(format string, args... interface{})
  4. /* 作用是记录日志,区别是是否支持格式化*/
  5. // 标记失败
  6. Fail()
  7. /* 仅标记当前测试状态为失败 */
  8. // 标记失败+ + 结束测试
  9. FailNow()
  10. // 标记失败 + 记录日志
  11. Error(args... interface{})
  12. Errorf(format string, args... interface{})
  13. /* 只标记测试状态为失败,不影响测试函数流程,不会结束测试 */
  14. // 标识失败 + 记录日志 + 结束测试
  15. Fatal(args... interface{})
  16. Fatalf(format string, args... interface{})
  17. // 跳过测试 + 记录日志
  18. Skip(args... interface{})
  19. Skipf(format string, args... interface{})
  20. // 跳过测试 + 结束测试
  21. SkipNow()

例子:以一个简单的加法函数为例子

  1. // main.go
  2. package main
  3. import (
  4. "fmt"
  5. )
  6. func main() {
  7. fmt.Println(3, 4)
  8. }
  9. func add(x, y int) int {
  10. return x + y
  11. }
  12. // main_test.go
  13. package main
  14. import (
  15. "testing"
  16. )
  17. func TestAdd(t *testing.T) {
  18. arg1 := 3
  19. arg2 := 4
  20. expected := 7
  21. result := add(arg1, arg2)
  22. if result != expected {
  23. t.Errorf("add(%d, %d) = %d, wanted %d", arg1, arg2, result, expected)
  24. }
  25. }
  26. /*
  27. 执行:go test -v
  28. 结果如下:
  29. === RUN TestAdd
  30. --- PASS: TestAdd (0.00s)
  31. PASS
  32. ok test 0.038s
  33. */

所谓单元测试,基本上就是为被测函数手动构造测试用例。

基准测试

基准测试是进行性能测试,使用 testing.B 结构。
常用标志:

  • -bench:后面接正则表达式,筛选符合条件的测试文件,-bench . 表示全测。
  • -benchtime:设置测试时间,默认 1 秒。如 -benchtime 5s 测试 5 秒,-benchtime 30x 表示只执行 30 次。

例子:观察 for i 和 for range 的性能差异

  1. // main.go
  2. package main
  3. import (
  4. "fmt"
  5. )
  6. var nums []int
  7. func main() {
  8. nums = make([]int, 10)
  9. forI()
  10. forRange()
  11. }
  12. func forI() {
  13. for i := 0; i < len(nums); i++ {
  14. fmt.Println(i, nums[i])
  15. }
  16. }
  17. func forRange() {
  18. for i, num := range nums {
  19. fmt.Println(i, num)
  20. }
  21. }
  22. // main_test.go
  23. package main
  24. import (
  25. "testing"
  26. )
  27. func BenchmarkForI(b *testing.B) {
  28. for i := 0; i < b.N; i++ {
  29. forI()
  30. }
  31. }
  32. func BenchmarkForRange(b *testing.B) {
  33. for i := 0; i < b.N; i++ {
  34. forRange()
  35. }
  36. }
  37. /*
  38. 执行:go test -bench .
  39. 结果如下:
  40. goos: windows
  41. pkg: test
  42. cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
  43. BenchmarkForI-12 656349938 1.817 ns/op
  44. BenchmarkForRange-12 656249076 1.835 ns/op
  45. ok test 2.815s
  46. */
  • 第一列函数名后面 -12 表示本次测试 runtime.GOMAXPROCS 被设置为 12,即同时运行 12 个 goroutine 并行。
  • 第二列的数字表示循环的次数,即 b.N 的实际取值,b.N 是由程序决定的,一般是 10 的指数级。
  • 第三列表示每次循环花费的时间,1.817 ns/op 表示平均每次循环花费 1.817 纳秒。

看起来好像性能差不多,如果自定义一个如下结构体,会看见天差地别!

  1. package main
  2. import (
  3. // "fmt"
  4. )
  5. type Person struct {
  6. Name string
  7. scores [4096]int
  8. }
  9. var persons []Person
  10. func main() {
  11. persons = make([]Person, 100000000)
  12. forI()
  13. forRange()
  14. }
  15. func forI() {
  16. tmp := 0
  17. for i := 0; i < len(persons); i++ {
  18. tmp = persons[i].scores[0]
  19. }
  20. _ = tmp
  21. }
  22. func forRange() {
  23. tmp := 0
  24. for _, person := range persons {
  25. tmp = person.scores[0]
  26. }
  27. _ = tmp
  28. }
  29. /*
  30. goos: windows
  31. goarch: amd64
  32. pkg: test
  33. cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
  34. BenchmarkForI-12 1000000000 0.5202 ns/op
  35. BenchmarkForRange-12 733372609 1.602 ns/op
  36. PASS
  37. ok test 2.094s
  38. */

性能差异初现,相差两倍左右。

示例测试

广泛用于 Go 源码和开源项目,目的是展示某个包或某个函数的用法。

测试原理

目前不打算学,太多了。