跳到主要内容

Unit Test(单元测试)

Go 使用标准库 testing 运行测试。生产代码放在普通 .go 文件中,测试文件以 _test.go 结尾;测试函数名以 Test 开头并接收 *testing.T

表格驱动测试

生产文件 score.go

package score

func Grade(value int) string {
if value >= 60 {
return "pass"
}
return "fail"
}

测试文件 score_test.go

package score

import "testing"

func TestGrade(t *testing.T) {
tests := []struct {
name string
input int
want string
}{
{name: "pass boundary", input: 60, want: "pass"},
{name: "fail boundary", input: 59, want: "fail"},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if got := Grade(test.input); got != test.want {
t.Fatalf("Grade(%d) = %q, want %q", test.input, got, test.want)
}
})
}
}

测试应验证可观察行为和失败边界,而不是复制实现细节。子测试名称应稳定、能定位场景;只有确认每个子测试互不共享可变状态时才使用 t.Parallel()

Fixture、示例和基准

共享 fixture 可以用 t.Helper 标记辅助函数,用 t.Cleanup 注册资源清理。ExampleXxxOutput: 注释会把示例同时作为测试;BenchmarkXxx 用于在固定输入下测量变化,不能直接外推生产吞吐。Go 1.18 起可使用 FuzzXxx 探索边界输入。

常用命令:

go test ./...
go test -cover ./...
go test -race ./...

并发测试应同时覆盖取消、超时和资源退出;竞态检测不能证明没有死锁或业务逻辑错误。

常见错误

  • 只测试成功路径,忽略错误、空输入、边界和取消。
  • 在循环子测试中共享可变变量,尤其是并发子测试。
  • 断言内部实现,导致无行为变化的重构也让测试失效。
  • 忽略清理函数的错误或让测试依赖当前工作目录。
  • 把覆盖率百分比或 benchmark 数字当作质量和生产性能的唯一结论。

小结

  • 表格驱动测试适合覆盖多组输入和边界。
  • 子测试、fixture、示例、基准、模糊测试和 -race 解决不同问题。
  • 测试必须验证失败路径,并保证资源和并发任务可以结束。