深色模式
Go单元测试
什么是单元测试
单元测试是对软件中最小的可测试部分(称为单元)进行验证,以确保其按预期工作。在Go中,单元通常是函数或方法。通过编写单元测试,可以在代码更改时快速发现问题,确保代码的稳定性和可靠性。
Go中的测试文件结构
在Go中,测试文件需要满足以下条件:
- 文件命名:测试文件必须以
_test.go
结尾。 - 包名:可以与被测试代码的包名相同,或以
_test
作为后缀。 - 测试函数:测试函数以
Test
开头,遵循TestXxx
的命名格式,其中Xxx
可以是任何名称,但首字母需大写。 - 函数签名:测试函数接受一个指向
testing.T
类型的指针作为参数,例如:func TestSomething(t *testing.T)
。
示例,为math.go
编写测试代码math_test.go
:
go
package math
// Add 函数返回两个整数的和
func Add(a, b int) int {
return a + b
}
go
package math
import "testing"
// TestAdd 测试 Add 函数
func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Add(2, 3) = %d; 想要 %d", result, expected)
}
}
编写基本的测试函数
测试函数用于验证代码的正确性。在测试函数中,可以使用testing
包提供的方法,如:
t.Error(args ...)
:报告错误但继续执行测试。t.Errorf(format string, args ...)
:格式化错误消息并报告错误。t.Fail()
:标记测试失败但继续执行。t.FailNow()
:标记测试失败并立即中止。t.Fatal(args ...)
:报告错误并中止测试。t.Fatalf(format string, args ...)
:格式化错误消息,报告错误并中止测试。t.Log(args ...)
:记录测试信息。
示例:
go
func TestSubtract(t *testing.T) {
result := Subtract(5, 3)
expected := 2
if result != expected {
t.Fatalf("Subtract(5, 3) = %d; 想要 %d", result, expected)
}
}
运行测试
在终端中,进入包含测试文件的目录,使用以下命令运行测试:
bash
go test
如果要查看测试的详细信息,可以使用-v
标志:
bash
go test -v
要运行特定的测试函数,可以使用-run
标志:
bash
go test -run TestAdd
表驱动测试
表驱动测试是一种编写测试的模式,使用一组输入和期望的输出来测试函数的多个场景。这种方式可以让测试代码更加简洁和可读。
示例:
go
func TestMultiply(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"正数相乘", 2, 3, 6},
{"零相乘", 0, 5, 0},
{"负数相乘", -1, 5, -5},
{"大数相乘", 100, 100, 10000},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Multiply(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Multiply(%d, %d) = %d; 想要 %d", tt.a, tt.b, result, tt.expected)
}
})
}
}
测试覆盖率
测试覆盖率用于衡量测试代码覆盖了被测试代码的多少。
查看覆盖率百分比:
bash
go test -cover
生成覆盖率报告:
bash
go test -coverprofile=coverage.out
go tool cover -html=coverage.out
上述命令将在浏览器中打开一个覆盖率报告的HTML页面,直观地显示哪些代码被测试覆盖,哪些没有。
基准测试
基准测试用于测量函数的性能。基准函数以Benchmark
开头,接受*testing.B
类型的参数。
示例:
go
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(1, 2)
}
}
运行基准测试:
bash
go test -bench=.
其中,-bench=.
表示运行所有基准测试。
示例函数
示例函数用于生成文档和展示代码的用法。示例函数以Example
开头,不需要参数或返回值。
示例:
go
func ExampleAdd() {
fmt.Println(Add(2, 3))
// Output: 5
}
在运行go test
时,Go会验证示例函数的输出是否与注释中的// Output:
一致。
编写好的测试的最佳实践
- 保持测试的独立性:测试之间不应互相依赖,每个测试应独立运行。
- 使用表驱动测试:当测试同一函数的多个输入输出时,表驱动测试可以提高代码的可读性和维护性。
- 覆盖边界条件:测试应涵盖正常情况、边界条件和异常情况。
- 使用辅助函数:当有重复的测试逻辑时,提取为辅助函数,减少代码重复。
- 清理测试环境:使用
defer
和t.Cleanup
清理测试中创建的资源,确保测试环境的整洁。 - 避免在测试中使用全局变量:全局变量可能导致测试之间的相互影响,尽量避免。
- 命名清晰:测试函数和测试用例的名称应清晰描述其测试的内容。