Go测试函数必须命名为func TestXxx(*testing.T),Xxx首字母大写且文件以_test.go结尾;失败用t.Error或t.Fatal,推荐表格驱动测试和t.Run分组。

Go 的 testing 库不依赖第三方,开箱即用,但写法和主流框架差异大——它靠命名约定驱动,不是靠注解或断言函数;测试函数必须以 Test 开头且接收 *testing.T,否则根本不会被 go test 发现。
如何命名和定义一个可运行的测试函数
Go 不识别 test_、_test 或任意前缀后缀,只认准 func TestXxx(*testing.T) 这一签名。Xxx 必须是大写字母开头(即导出名),否则 go test 会静默跳过。
-
func TestAdd(t *testing.T)✅ 可执行 -
func testAdd(t *testing.T)❌ 不会被发现 -
func Test_add(t *testing.T)❌ Xxx 首字母必须大写 -
func TestAdd(t int)❌ 参数类型必须是*testing.T
测试文件也需遵守命名规则:必须以 _test.go 结尾,且和待测代码在同一个包内(比如 math.go 对应 math_test.go)。
如何在测试中报告失败并提前退出
*testing.T 提供了 Fail()、FailNow()、Error()、Fatal() 等方法,区别在于是否终止当前函数执行:
立即学习“go语言免费学习笔记(深入)”;
-
t.Error("msg")打印错误但继续执行后续语句 -
t.Fatal("msg")打印错误并立即return,不再跑该测试函数剩余逻辑 -
t.Errorf("want %v, got %v", want, got)是最常用写法,支持格式化输出 - 避免混用
log.Print或fmt.Println——它们不会被标记为失败,也不会计入测试统计
示例:
func TestDivide(t *testing.T) {
result := divide(10, 0)
if result != 0 {
t.Fatalf("divide by zero should return 0, got %v", result)
}
}
如何为同一函数写多个测试用例(表格驱动)
Go 官方推荐“表格驱动测试”(table-driven tests),用切片定义多组输入/期望值,避免重复写 TestXxx 函数。关键点是:每个子测试用 t.Run() 包裹,便于定位失败项,且支持并发执行(加 t.Parallel())。
- 子测试名建议可读,如
"positive_numbers",不要用索引"case 0" -
t.Run()内部的逻辑独立,t.Fatal只终止当前子测试,不影响其他子测试 - 若想让子测试并发运行,必须在
t.Run()内第一行调用t.Parallel()
示例:
func TestAddTable(t *testing.T) {
tests := []struct {
a, b, want int
}{
{1, 2, 3},
{-1, 1, 0},
{0, 0, 0},
}
for _, tt := range tests {
t.Run(fmt.Sprintf("%d+%d", tt.a, tt.b), func(t *testing.T) {
t.Parallel()
got := Add(tt.a, tt.b)
if got != tt.want {
t.Errorf("Add(%d,%d) = %d, want %d", tt.a, tt.b, got, tt.want)
}
})
}
}
如何运行测试并查看覆盖率和详细输出
go test 命令本身足够轻量,但参数组合决定你能看到什么信息:
-
go test—— 默认只运行当前目录下所有*_test.go中的TestXxx函数 -
go test -v—— 显示每个测试函数名和t.Log()输出,便于调试 -
go test -run=^TestAdd$—— 正则匹配测试名,精确运行单个函数(^和$防止误匹配TestAddWithCache) -
go test -cover—— 输出整体测试覆盖率(如coverage: 85.7% of statements) -
go test -coverprofile=c.out && go tool cover -html=c.out—— 生成 HTML 覆盖率报告,标出未覆盖的行
注意:-cover 统计的是「被至少一个测试执行到的源码行」,不是「是否断言正确」;一行代码即使逻辑错,只要被执行了就算覆盖。
最容易被忽略的是:子测试名重复会导致后一个覆盖前一个,t.Run("same_name", ...) 在同一作用域内多次调用时,最终报告里只显示最后一次的结果;还有,测试文件里如果写了 func BenchmarkXxx(*testing.B) 或 func ExampleXxx(),它们不会干扰单元测试,但需要对应参数类型,写错也不会报错,只是静默无效。










