介绍内置的 Go 测试支持
在本节课中,我们将讨论 Go 自带的测试功能。
Go 作为一种务实的语言,提供了丰富的测试支持,包括定义和运行测试的方法、对代码运行时间和内存使用情况的基准测试、提供使用示例,甚至测试覆盖率。我们将涵盖的主题包括 testing 包、go test 命令,以及编写和运行测试。
testing 包
Go 测试从 testing 包开始(也经常以它结束)。该包定义了用于编写 Go 包自动化测试的类型和函数。go test 命令完全支持 testing 包,并设计为运行你的测试并捕获结果。稍后我们会介绍其他一些测试框架,但它们都基于 testing 包之上。以下是你可以立即获得的一些内置功能:
- 自动发现测试和基准测试
- 嵌套测试和表驱动测试
- 跳过测试
- 验证示例代码
- 包级别的设置和清理
好了,让我们直接进入正题,看看如何编写测试。
编写测试
如果你熟悉 xUnit 风格的测试,那么准备好迎接一些新东西吧。Go 测试使用 testing 包中定义的特殊类型,并且必须实现特定的签名:
1func TestFoo(t *testing.T) { 2}
传递给测试的 testing.T 类型具有多种方法,你可以调用这些方法来告知测试基础设施测试是否失败,包括格式化和记录错误消息、跳过测试、运行子测试等。
对于基准测试,签名则不同:
1func BenchmarkFoo(*testing.B) { 2}
testing.B 类型的作用类似于 testing.T。它会被传入每个基准测试,并具有大量有助于基准测试的功能,例如报告内存分配、报告指标、处理计时器以及并行运行基准测试。
T 和 B 类型共享一个共同接口,非常富有想象力地命名为 TB:
1type TB interface { 2 Error(args ...interface{}) 3 Errorf(format string, args ...interface{}) 4 Fail() 5 FailNow() 6 Failed() bool 7 Fatal(args ...interface{}) 8 Fatalf(format string, args ...interface{}) 9 Log(args ...interface{}) 10 Logf(format string, args ...interface{}) 11 Name() string 12 Skip(args ...interface{}) 13 SkipNow() 14 Skipf(format string, args ...interface{}) 15 Skipped() bool 16 Helper() 17}
由于 multi-git 只控制 git,我们不会关注性能,也不会编写任何基准测试。 目前理论就讲这么多。让我们来写一些测试吧。
编写简单的单元测试
首先,单元测试应放在与被测代码相同的包中,文件名为 _test.go。这使得 Go 测试命令可以轻松发现所有需要运行的测试。
下面是一个非常简单的 Go 文件,其中包含一个 CalcArea() 函数,用于计算矩形面积。它接受两个整数作为宽度和高度,并返回它们的乘积,这是计算矩形面积的一种绝佳方式。
如果宽度或高度为零或负数,则返回错误,因为任何自尊的矩形都必须有正的宽度和高度。
1package main 2 3import ( 4 "errors" 5) 6 7var errorMessage = "width and height must be positive" 8 9// 计算矩形面积 10func CalcArea(w, h int) (int, error) { 11 if w < 1 || h < 1 { 12 return 0, errors.New(errorMessage) 13 } 14 return w * h, nil 15}
为了测试这个函数,我们需要至少两个测试用例:一个是当宽度和高度为正数时的正常情况,另一个是当宽度或高度不为正数时的情况。下面是成功情况的代码。注意,它与 CalcArea() 函数位于同一个 main 包中。它导入了 testing 包,并定义了一个符合测试函数接受签名的测试函数。然后,它检查 CalcArea(3, 5) 是否返回 15 且没有错误。如果不是这样,它将使用 t.Error() 或 t.Errorf() 方法报告测试失败。如果没有发生这些错误,测试将通过。
1package main 2 3import ( 4 "testing" 5) 6 7func TestCalcAreaSuccess(t *testing.T) { 8 result, err := CalcArea(3, 5) 9 if err != nil { 10 t.Error("Expected CalcArea(3, 5) to succeed") 11 } else if result != 15 { 12 t.Errorf("CalcArea(3, 5) returned %d. Expected 15", result) 13 } 14}
下面是失败情况的测试代码。调用 CalcArea(-3, 6) 时应该失败并返回错误。同时,测试还检查错误信息是否正确。
1func TestCalcAreaFail(t *testing.T) { 2 _, err := CalcArea(-3, 6) 3 if err == nil { 4 t.Error("Expected CalcArea(-3, 6) to return an error") 5 } 6 7 if err.Error() != errorMessage { 8 t.Error("Expected error to be: " + errorMessage) 9 } 10}
现在你已经知道如何编写测试了,让我们尝试运行它们,亲眼看看效果。
No configuration found for ID: 3b2d7234829183b8fd7af475bac7064180f177dd726078836f085c2e5dad2ac5
好了,你对测试的样子已经有了不错的了解,但如果我们要添加大量类似的测试用例,代码会变得相当冗长。别担心,我的朋友,testing 包正好有解决方案:表驱动测试。
编写表驱动测试
表驱动测试解决了常见场景:希望在不同的输入集合上运行相同的测试。你可以不断重复复制上面的测试代码,但这很快就会变得繁琐且重复,而且在复制粘贴时很容易出错。因此,我们可以创建一个包含所有输入的表格,并遍历这些值来运行相同的测试。在这里,我们创建一个结构体列表,其中包含输入(即宽度和高度)以及期望的结果。然后,测试函数的主体部分会使用所有输入值调用 CalcArea() 并检查结果。
让我们试试看。
No configuration found for ID: 10054b92ea4c7ec10b279d9fb7e2507cdbc1fb72d34ca3ee551e62cc21330202