您的位置:首页 > 编程语言 > Go语言

Go语言学习笔记 - 第十一章 单元测试(The Go Programming Language)

2020-01-13 06:07 369 查看

第十一章 单元测试

  • 让软件的复杂性可得到控制两个有效的方法: 代码在被正式部署前需要进行代码评审
  • 自动化测试
  • Go语言的测试技术是相对低级的,。它依赖一个
    go test
    测试命令和一组按照约定方式编写的测试函数,测试命令可以运行这些测试函数
  • 11.1go test

    划重点

    • go test
      命令是一个按照一定的约定和组织的测试代码的驱动程序。在包目录内,所有以
      _test.go
      为后缀名的源文件并不是
      go build
      构建包的一部分,它们是
      go test
      测试的一部分。
    • 在*_test.go文件中,有三种类型的函数:测试函数、基准测试函数、示例函数。
    • 测试函数是以Test为函数名前缀的函数,用于测试程序的一些逻辑行为是否正确;
      go test
      命令会调用这些测试函数并报告测试结果是
      PASS
      FAIL
    • 基准测试函数是以Benchmark为函数名前缀的函数,它们用于衡量一些函数的性能;
      go test
      命令会多次运行基准函数以计算一个平均的执行时间
    • 示例函数是以Example为函数名前缀的函数,提供一个由编译器保证正确性的示例文档
    • go test
      命令会遍历所有的
      *_test.go
      文件中符合上述命名规则的函数,然后生成一个临时的
      main
      包用于调用相应的测试函数,然后构建并运行、报告测试结果,最后清理测试中生成的临时文件。

    11.2测试函数

    划重点

    • 每个测试函数必须导入testing包。测试函数有如下的签名:
    func TestName(t *testing.T) {
    // ...
    }
    • 测试函数的名字必须以Test开头,可选的后缀名必须以大写字母开头,其中t参数用于报告测试失败和附加的日志信息:
    func TestSin(t *testing.T) { /* ... */ }
    func TestCos(t *testing.T) { /* ... */ }
    func TestLog(t *testing.T) { /* ... */ }
    • go test
      命令如果没有参数指定包那么将默认采用当前目录对应的包(和
      go build
      命令一样)
    • go test -v
      ,参数
      -v
      可用于打印每个测试函数的名字和运行时间
    • go test -run
      , 参数
      -run
      对应一个正则表达式,只有测试函数名被它正确匹配的测试函数才会被
      go test
      测试命令运行
    • 失败测试的输出并不包括调用
      t.Errorf
      时刻的堆栈调用信息。和其他编程语言或测试框架的
      assert
      断言不同,
      t.Errorf
      调用也没有引起
      panic
      异常或停止测试的执行
    • 可以使用
      t.Fatal
      t.Fatalf
      停止当前测试函数。它们必须在和测试函数同一个
      goroutine
      内调用

    常用库及方法

    • (*testing.T).Error
      (*testing.T).Errorf

    11.2.1随机测试

    划重点

    • 随机测试是通过构造更广泛的随机输入来测试探索函数的行为
    • 两种处理策略帮助获取希望的结果: 第一个是编写另一个对照函数
    • 第二种是生成的随机输入的数据遵循特定的模式,

    常用库及方法

    • time.Now().UTC().UnixNano()
      rand.New(rand.NewSource(seed))
    • (*testing.T).Logf

    11.2.2测试一个命令

    划重点

    • go test
      可以用来测试可执行程序,因为
      main
      包可以作为一个包被测试器代码导入。注意的此时的测试代码和产品代码在同一个包。
    • 对于
      main
      包,也有对应的
      main
      入口函数,但是在测试的时候
      main
      包只是
      TestEcho
      测试函数导入的一个普通包,里面
      main
      函数并没有被导出,而是被忽略的。

    11.2.3白盒测试

    划重点

    • 黑盒测试只需要测试包公开的文档和API行为,内部实现对测试代码是透明的
    • 白盒测试(clear box)有访问包内部函数和数据结构的权限
    • 处理模式可以用来暂时保存和恢复所有的全局变量,包括命令行标志参数、调试选项和优化参数;安装和移除导致生产代码产生一些调试信息的钩子函数;还有有些诱导生产代码进入某些重要状态的改变,比如超时、错误,甚至是一些刻意制造的并发行为等因素。
    • go test
      命令并不会同时并发地执行多个测试

    常用库及方法

    • smtp.PlainAuth
      smtp.SendMail

    11.2.4扩展测试包

    划重点

    • 可以通过测试扩展包的方式解决循环依赖的问题
    • 扩展测试包可以更灵活的编写测试,特别是集成测试(需要测试多个组件之间的交互)
    • go list
      命令查看包对应目录中哪些Go源文件是产品代码,哪些是包内测试,还哪些测试扩展包。我们以
      fmt
      包作为一个例子:
      GoFiles
      表示产品代码对应的Go源文件列表;也就是
      go build
      命令要编译的部分
    • TestGoFiles
      表示的是fmt包内部测试测试代码,以
      _test.go
      为后缀文件名
    • XTestGoFiles表示的是属于测试扩展包的测试代码,也就是fmt_test包
    $ go list -f={{.GoFiles}} fmt
    [doc.go format.go print.go scan.go]
    -----------------------------------
    $ go list -f={{.TestGoFiles}} fmt
    [export_test.go]
    -----------------------------------
    $ go list -f={{.XTestGoFiles}} fmt
    [fmt_test.go scan_test.go stringer_test.go]

    11.2.5编写有效的测试

    划重点

    • 测试不仅报告调用的具体函数、它的输入和结果的意义;并且打印的真实返回的值和期望返回的值;并且即使断言失败依然会继续尝试运行更多的测试。

    11.2.6避免的不稳定的测试

    划重点

    11.3测试覆盖率

    划重点

    • 由测试驱动触发运行到的被测试函数的代码数目称为测试的覆盖率
    • 语句的覆盖率是指在测试中至少被运行一次的代码占总代码数的比例。
    • go test
      命令中集成的测试覆盖率工具,可以用来度量代码的测试覆盖率,帮助我们识别测试和我们期望间的差距。
    • go tool cover
      显示coverage的使用方法。
      go tool
      命令运行Go工具链的底层可执行程序。这些底层可执行程序放在
      $GOROOT/pkg/tool/${GOOS}_${GOARCH}
      目录。
    • go test -run=Coverage -coverprofile=c.out gopl.io/ch7/eval
    • go test -cover
      生成摘要
    • -covermode=count
      标志参数,那么将在每个代码块插入一个计数器而不是布尔标志量。在统计结果中记录了每个块的执行次数,这可以用于衡量哪些是被频繁执行的热点代码。
    • $ go tool cover -html=c.out
      打印了测试日志,生成一个HTML报告

    11.4基准测试

    划重点

    • 基准测试函数和普通测试函数写法类似,但是以
      Benchmark为
      前缀名,并且带有一个
      *testing.B
      类型的参数;
      *testing.B
      参数除了提供和
      *testing.T
      类似的方法,还有额外一些和性能测量相关的方法。它还提供了一个整数
      N
      ,用于指定操作执行的循环次数。
    • 默认情况下不运行任何基准测试。
      我们需要通过
      -bench
      命令行标志参数手工指定要运行的基准测试函数。该参数是一个正则表达式,用于匹配要执行的基准测试函数的名字,默认值是空的。其中
      “.”
      模式将可以匹配所有基准测试函数。
      go test -bench=.
    • -benchmem
      命令行标志参数将在报告中包含内存的分配数据统计

    11.5剖析

    划重点

    • CPU分析文件标识了函数执行时所需要的CPU时间。当前运行的系统线程在每隔几毫秒都会遇到操作系统的中断事件,每次中断时都会记录一个分析文件然后恢复正常的运行。
    • 堆分析则记录了程序的内存使用情况。每个内存分配操作都会触发内部平均内存分配例程,每个512KB的内存申请都会触发一个事件。
    • 阻塞分析则记录了goroutine最大的阻塞操作,例如系统调用、管道发送和接收,还有获取锁等。分析库会记录每个goroutine被阻塞时的相关操作。
    $ go test -cpuprofile=cpu.out
    $ go test -blockprofile=block.out
    $ go test -memprofile=mem.out
    $ go tool pprof
    • 基准测试会默认包含单元测试,这里我们用
      -run=NONE
      参数禁止单元测试。(这一部分需要具体查看原文的使用介绍)

    11.6示例函数

    划重点

    • 示例函数没有函数参数和返回值。
    • Example示例函数将是包文档的一部分。
    • 示例函数有三个用处: 最主要的一个是作为文档。
    • go test
      执行测试的时候也运行示例函数测试。测试结果会和函数内
      // Output:
      格式的注释相比较,检查结果是否匹配。
    • 提供一个真实的演练场。

    后面两节暂不做笔记,普通Go程序员暂时用不上

    • 点赞
    • 收藏
    • 分享
    • 文章举报
    rabbit0206 发布了13 篇原创文章 · 获赞 3 · 访问量 126 私信 关注
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: