- 概述
- testing.T 核心方法
- 测试控制方法
- 辅助方法
- 子测试方法
- 并行测试
- 性能测试相关
- CLI 命令参数
- 最佳实践
- 示例
在 Go 语言中,testing.T 是测试包的核心类型,用于编写单元测试。它提供了丰富的方法来控制测试流程、报告测试结果和处理测试失败。本文档详细介绍 testing.T 的 API 以及测试命令的 CLI 参数。
| 方法 | 说明 | 用法 |
|---|
Error(args ...interface{}) | 记录错误但继续执行测试 | t.Error("测试失败") |
Errorf(format string, args ...interface{}) | 格式化记录错误但继续执行 | t.Errorf("期望 %d, 实际 %d", expected, actual) |
Fail() | 标记测试失败但继续执行 | t.Fail() |
FailNow() | 标记测试失败并立即停止执行 | t.FailNow() |
Fatal(args ...interface{}) | 记录错误并立即停止执行 | t.Fatal("测试失败,停止执行") |
Fatalf(format string, args ...interface{}) | 格式化记录错误并立即停止执行 | t.Fatalf("期望 %d, 实际 %d", expected, actual) |
Log(args ...interface{}) | 记录信息,仅在测试失败时显示 | t.Log("测试信息") |
Logf(format string, args ...interface{}) | 格式化记录信息,仅在测试失败时显示 | t.Logf("值为: %v", value) |
Skip(args ...interface{}) | 跳过当前测试并继续执行 | t.Skip("跳过测试") |
Skipf(format string, args ...interface{}) | 格式化跳过当前测试 | t.Skipf("跳过测试: %s", reason) |
SkipNow() | 跳过当前测试并继续执行 | t.SkipNow() |
| 方法 | 说明 | 用法 |
|---|
Failed() | 返回测试是否失败 | if t.Failed() { /* 处理失败 */ } |
Skipped() | 返回测试是否被跳过 | if t.Skipped() { /* 处理跳过 */ } |
Name() | 返回测试名称 | name := t.Name() |
Cleanup(func()) | 注册测试完成后执行的清理函数 | t.Cleanup(func() { /* 清理代码 */ }) |
| 方法 | 说明 | 用法 |
|---|
Setenv(key, value string) | 设置环境变量 | t.Setenv("API_KEY", "test123") |
TempDir() | 创建临时目录,测试完成后自动删除 | tempDir := t.TempDir() |
Parallel() | 标记测试可以并行执行 | t.Parallel() |
| 方法 | 说明 | 用法 |
|---|
SetTimeout(d time.Duration) | 设置测试超时时间 | t.SetTimeout(5 * time.Second) |
Deadline() | 返回测试的截止时间 | deadline := t.Deadline() |
| 方法 | 说明 | 用法 |
|---|
Context() | 返回测试上下文 | ctx := t.Context() |
Helper() | 标记当前函数为测试辅助函数 | t.Helper() |
| 方法 | 说明 | 用法 |
|---|
Helper() | 标记当前函数为辅助函数,错误信息会显示调用处 | func helper() { t.Helper(); t.Error("错误") } |
Run(name string, f func(t *testing.T)) bool | 运行子测试 | t.Run("子测试", func(t *testing.T) { /* 测试代码 */ }) |
func TestMain(t *testing.T) {
t.Run("TestCase1", func(t *testing.T) {
// 测试代码
})
t.Run("TestCase2", func(t *testing.T) {
// 测试代码
})
}
| 方法 | 说明 | 用法 |
|---|
Run(name string, f func(t *testing.T)) bool | 运行子测试并返回是否通过 | passed := t.Run("子测试", func(t *testing.T) {}) |
func TestParallel(t *testing.T) {
t.Parallel() // 标记测试可以并行执行
// 测试代码
}
func TestSubParallel(t *testing.T) {
t.Run("Sub1", func(t *testing.T) {
t.Parallel()
// 子测试1代码
})
t.Run("Sub2", func(t *testing.T) {
t.Parallel()
// 子测试2代码
})
}
- 并行测试会共享测试环境,需要注意并发安全
- 子测试的并行执行需要在子测试内部调用
t.Parallel() - 主测试的
t.Parallel() 会使所有子测试并行执行
| 方法 | 说明 | 用法 |
|---|
ReportAllocs() | 启用内存分配统计 | b.ReportAllocs() |
ResetTimer() | 重置计时器,用于排除初始化时间 | b.ResetTimer() |
StopTimer() | 停止计时器 | b.StopTimer() |
StartTimer() | 开始计时器 | b.StartTimer() |
SetBytes(n int64) | 设置每次操作的字节数,用于计算带宽 | b.SetBytes(1024) |
go test [flags] [packages]
| 参数 | 说明 | 用法 |
|---|
-v | 详细输出模式,显示测试执行过程 | go test -v |
-run regexp | 只运行匹配正则表达式的测试 | go test -run TestSomething |
-bench regexp | 运行匹配正则表达式的基准测试 | go test -bench BenchmarkSomething |
-benchmem | 显示基准测试的内存分配情况 | go test -bench . -benchmem |
-timeout duration | 设置测试超时时间 | go test -timeout 30s |
-cover | 启用代码覆盖率分析 | go test -cover |
-coverprofile file | 输出覆盖率分析到文件 | go test -coverprofile=coverage.out |
-count n | 运行测试的次数 | go test -count=5 |
-parallel n | 设置并行测试的最大数量 | go test -parallel=4 |
-short | 运行短时间的测试 | go test -short |
-race | 启用数据竞争检测 | go test -race |
-cpu list | 设置测试使用的 CPU 数量 | go test -cpu=1,2,4 |
-args | 传递参数给测试程序 | go test -args -verbose |
| 参数 | 说明 | 用法 |
|---|
-cover | 启用覆盖率分析 | go test -cover |
-coverprofile=file | 输出覆盖率数据到文件 | go test -coverprofile=cover.out |
-covermode=mode | 设置覆盖率模式(set、count、atomic) | go test -covermode=atomic |
-coverpkg=patterns | 指定要分析覆盖率的包 | go test -coverpkg=./... |
| 参数 | 说明 | 用法 |
|---|
-i | 安装测试依赖但不运行测试 | go test -i |
-json | 以 JSON 格式输出测试结果 | go test -json |
-c | 编译测试但不运行 | go test -c |
-exec xprog | 使用指定的程序运行测试 | go test -exec ./wrapper.sh |
- 测试文件命名:以
_test.go 结尾 - 测试函数命名:以
Test 开头,后跟大写字母 - 子测试:使用
t.Run() 组织相关测试用例 - 表格驱动测试:使用结构体切片组织测试数据
- 使用
Errorf 提供详细信息:t.Errorf("期望 %v, 实际 %v", expected, actual) - 使用
Fatal 停止关键测试:t.Fatal("无法继续测试") - 使用
Skip 处理条件测试:if !condition { t.Skip("条件不满足") }
- 只在无共享状态时使用并行:避免并发安全问题
- 合理设置并行度:
go test -parallel=4 - 子测试并行:在子测试中单独调用
t.Parallel()
- 使用
ResetTimer() 排除初始化时间 - 使用
ReportAllocs() 分析内存分配 - 设置合理的基准测试循环:
for i := 0; i < b.N; i++ { /* 测试代码 */ }
package main
import (
"testing"
)
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"正数相加", 1, 2, 3},
{"负数相加", -1, -2, -3},
{"混合相加", 1, -2, -1},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Add(%d, %d) = %d; 期望 %d", tt.a, tt.b, result, tt.expected)
}
})
}
}
func Add(a, b int) int {
return a + b
}
package main
import (
"testing"
"time"
)
func TestParallelExamples(t *testing.T) {
// 主测试不并行,子测试并行
t.Run("Parallel1", func(t *testing.T) {
t.Parallel()
time.Sleep(100 * time.Millisecond)
t.Log("Parallel1 完成")
})
t.Run("Parallel2", func(t *testing.T) {
t.Parallel()
time.Sleep(100 * time.Millisecond)
t.Log("Parallel2 完成")
})
t.Run("Sequential", func(t *testing.T) {
// 这个子测试不并行
time.Sleep(100 * time.Millisecond)
t.Log("Sequential 完成")
})
}
package main
import (
"testing"
)
func BenchmarkAdd(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
Add(1, 2)
}
}
func BenchmarkAddParallel(b *testing.B) {
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
Add(1, 2)
}
})
}
# 运行所有测试
go test
# 运行特定测试
go test -run TestAdd
# 运行匹配正则表达式的测试
go test -run "TestAdd|TestSub"
# 详细输出
go test -v
# 运行基准测试
go test -bench .
# 运行基准测试并显示内存分配
go test -bench . -benchmem
# 启用代码覆盖率
go test -cover
# 输出覆盖率到文件并查看
go test -coverprofile=cover.out
go tool cover -html=cover.out
# 启用数据竞争检测
go test -race
# 设置超时时间
go test -timeout 30s
# 并行运行测试
go test -parallel=4
testing.T 提供了丰富的 API 来编写和控制测试,包括:
- 断言方法:用于验证测试结果
- 控制方法:用于管理测试流程
- 子测试:用于组织测试用例
- 并行测试:用于提高测试效率
- 性能测试:用于分析代码性能
通过合理使用这些 API 和 CLI 参数,可以编写高质量的测试代码,确保代码的可靠性和性能。
- 使用
t.Run() 组织子测试,提高测试可读性 - 使用表格驱动测试,减少重复代码
- 合理使用并行测试,提高测试速度
- 使用
t.Cleanup() 确保资源正确清理 - 利用 CLI 参数控制测试行为,如覆盖率分析、数据竞争检测等
通过掌握这些技巧,可以编写更加高效、可靠的 Go 测试代码。