测试与基准测试的区别

本章我们将要讨论的最后一个概念是基准测试。testing 包提供了对基准测试的支持,通过 testing.B 类型进行操作。基准测试的签名与测试非常相似:

func BenchmarkName(b *testing.B) {
    // implementation
}

这个签名突出了 Go 基准测试的一些要求:

  • 基准测试是导出的函数,其名称以 Benchmark 开头。

  • Benchmark 名称可以包含一个额外的后缀,用于指定测试覆盖的内容。后缀也必须以大写字母开头,正如我们在 Name 中看到的那样。

  • Benchmarks 必须接收一个 *testing.B 类型的参数。正如我们目前所解释的,这是测试与测试运行器交互的方式。你可以随意命名该参数,但 Go 开发者通常使用 b 来表示它。

  • Benchmarks 不能有返回类型。

基准测试是一个重要的 Go 性能分析工具

测试验证程序的功能,而基准测试验证代码的性能。在你的测试策略中,应该同时使用这两者。

基准测试也可以通过 go test 命令运行,但我们必须指定测试运行器,我们对基准测试感兴趣,使用 –bench 标志。我们必须提供一个正则表达式,匹配我们想要运行的包。我们可以通过以下命令运行当前目录下的所有基准测试:

go test -bench .

testing.B 类型也可以访问记录错误和标记测试失败的方法,正如我们在 testing.T 类型的介绍中看到的那样:b.Errorb.Errorfb.Fatalb.Fatalf。与测试类似,基准测试也存在于测试文件中,这些文件必须以 _test.go 为后缀,以便 Go 测试运行器能够识别。

让我们在 engine_test.go 文件中为 Add 函数编写一个基准测试:

func BenchmarkAdd(b *testing.B) {
    e := calculator.Engine{}
    // 运行 Add 函数 b.N 次
    for i := 0; i < b.N; i++ {
        e.Add(2, 3)
    }
}

BenchmarkAdd 示例中,Add 函数会在循环中运行 b.N 次。Go 的测试运行器会控制 N 的值,并会逐渐增加它,直到它认为测量结果已经稳定。

与所有性能测试一样,你应该小心在本地机器上运行基准测试。由于你的计算机正在处理其他任务,基准测试的测量结果可能会有很大差异。

现在,我们运行基准测试,看到如下输出:

$ go test -bench . ./chapter02/calculator -v
pkg: github.com/PacktPublishing/Test-Driven-Developmentin-Go/chapter02/calculator
cpu: Intel(R) Core(TM) i5-8279U CPU @ 2.40GHz
BenchmarkAdd-8     1000000000    0.2684 ns/op
PASS
ok      github.com/PacktPublishing/Test-Driven-Developmentin-Go/chapter02/calculator 0.408s

基准测试运行的输出突出了以下内容:

  • 基准测试的名称:BenchmarkAdd

  • 用于运行基准测试的 CPU 核心数量,作为测试名称的后缀:8

  • 基准测试执行的次数:1000000000

  • 单次测试迭代的平均时间,以纳秒为单位:0.2684 ns/op

我们的函数非常简单,因此运行时间非常短。我们将在第 8 章《测试微服务架构》中探索更复杂的基准测试示例。