Go 单元测试 testing 包详解(testing.T、testing.M、testing.B、testing.PB)
Go 单元测试 testing 包详解
简介
单元测试(Unit Testing)是指对软件中的最小可测试单元(通常是函数、方法或类)进行验证,以确保其行为符合预期;是开发人员开发中、需求交付前必备的一项工作
Go 内置了轻量级的测试框架,通过标准库 testing
提供支持,无需额外依赖。
go 的单元测试文件命名规则:默认是原文件名称+_test.go 结尾
执行单元测试:
命令 | 作用 |
---|---|
go test 【包名】或 go test . | 运行当前package内的所有用例 |
go test ./… 或 go test 【目录名】/… | 递归执行当前目录下所有用例: |
go test -v [单元测试文件]. // 如 go test -v foo_test.go | 运行指定文件单元测试 |
go test -v [单元测试文件] -run [单元测试函数]. | 运行指定单元测试用例://如 go test -v foo_test.go -run TestFoo |
go test -bench . | 压测 |
执行单元测试时出现 调用函数 undefined 的解决方法:
如 执行 ******_test.go 下的TestXj函数,调用 ******.go 文件下的 Xj 函数
go test -v ******_test.go -run TestXj ———> 会出现Xj函数undefined 更换,加上调用函数的文件: go test -v ******_test.go ******.go -run TestXj
在 Goland 中,直接点击绿色按钮直接就可以运行了
一、testing.T
一般用在函数、方法的单元测试
代码例子:
func TestT(t *testing.T) {
t.Cleanup(func() {
// 将在所有子函数执行后执行
fmt.Println("测试Cleanup......")
})
t.Run("t.Fail()", func(t *testing.T) {
Expect(t).Equal(true, false)
})
t.Run("Setenv", func(t *testing.T) {
t.Setenv("******", "******")
getenv := os.Getenv("******")
t.Log("getenv:", getenv)
})
t.Run("v1", func(t *testing.T) {
name := t.Name()
t.Log(name)
dir := t.TempDir()
t.Log(dir)
})
}
t.Run() // 执行子单元测试 t.Log() // 输出日志,t.Logf() t.Name() // 输出单元测试的名称 t.Parallel() // 所有有t.Parallel()调用的单元测试 并行执行 t.TempDir() // 返回一个临时用于测试的目录地址,每次执行都不一样 t.Cleanup() // Cleanup 注册一个在测试(或子测试)及其所有子测试完成时要调用的函数。 清理函数将在最后添加,首先调用的顺序中调用 t.Setenv() // 设置测试环境变量,测试完成后自动清除,不能用于有t.Parallel()的测试函数 t.Helper() // 用于标注该函数是帮助函数,报错时将输出帮助函数调用者的信息,而不是帮助函数的内部信息,比如A函数是公共函数,B,C都是测试函数,且都调用A函数,那么假设A报错了,那并不知道是B还是C调用,所以此时就需要在A函数内部加上次标注,让报错信息更准确,有助于定位。 t.Fail() // 函数出现失败之后仍然执行 t.FailNow() // 函数出现失败之后结束 t.Deadline() // 无 t.SkipNow() // 跳过当前测试 t.Skiped() // 检测是否跳过 // 打印类 t.Log() // 打印日志 t.Logf() // 打印日志 format (printf 类似) t.Error() / Errorf() // 报告出错继续 [ Log / Logf + Fail ] t.Fatel() / Fatelf() 报告出错终止 [ Log / Logf + FailNow ]
二、testing.M
可以用来执行一些公共类单元测试,比如单元测试的前置、后置调用等
所有单元测试执行前都会先执行 testing.M 的单元测试
func PrintHelloWorld() {
fmt.Println("hello world")
}
对 PrintHelloWorld 进行单元测试
// TestMain 固定名称
func TestMain(m *testing.M) {
before() // 运行前
code := m.Run() //运行测试。 它返回一个退出代码以传递给 os.Exit。
after() // 运行后
os.Exit(code) //退出码
}
func TestPrintHelloWorld(t *testing.T) {
PrintHelloWorld()
}
func after() {
fmt.Println("单元测试执行结束...")
}
func before() {
fmt.Println("单元测试执行开始...")
}
不管你是运行测试【TestPrintHelloWorld】还是运行【TestMain】,输出的结果都一样:
单元测试执行开始...
=== RUN TestPrintHelloWorld
hello world
--- PASS: TestPrintHelloWorld (0.00s)
PASS
单元测试执行结束...
说明 TestMain 是单元测试的主入口函数,不管哪个单元测试执行,都会先执行 TestMain
或者你可以理解为:当你执行任意一个单元测试,比如这里的 TestPrintHelloWorld,go testing 会将 TestPrintHelloWorld 作为参数传入 TestMain(不存在,则执行运行),也就是 m 实际上等于 TestPrintHelloWorld,
TestMain 非常好用,可以用在单元测试启动前准备数据:mysql、redis连接、造数据;执行后清理数据
三、testing.B
Go 支持基准测试,函数名以 Benchmark
开头:
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(1, 2)
}
}
go test -bench=.
goos: darwin goarch: arm64 pkg: it******.com/learn-go cpu: Apple M1 Pro BenchmarkAdd BenchmarkAdd-10 515173099 2.264 ns/op PASS
go 的基准测试非常好用,是检验你的代码好坏的非常重要的工具,比如你对函数做了性能优化,那么就可以进行基准测试,对比优化前后的差异,从而得出优化结论
关于参数说明请移步文章:go 如何进行 Benchmark 基准测试
四、testing.PB
testing.PB
(Pointer to Benchmark
)是 testing.B.RunParallel()
内部使用的类型,用于协调并行执行的基准测试。
开发者通常不直接创建它,而是通过 b.RunParallel()
间接使用。所以一般使用的比较少,了解即可
func BenchmarkParallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() { // 自动分配迭代任务
Sum(1, 2) // 被测函数
}
})
}
用 b.RunParallel()
+ testing.PB
模拟多 goroutine 负载。
其他
执行测试参数:–test-short
go test -v ./... -test.short
如果执行单元测试时加上参数:-test.short
那么在代码中可以使用判断:
if testing.Short() { // 语句
// 当前时单元测试模式
}
testing.Short() 经常来用判断 go 当前的执行模式是不是单元测试执行
总结
testing.M | 测试测试的 main 主入口 |
---|---|
testing.T | 对函数/方法进行单元测试 |
testing. B | 对性能进行的基准测试 |
testing.PB | 并发基准测试 |