测试
翻译整理自 Testing 与 Testacular - Add test coverage。
Go 内置测试框架,无需第三方库。
1. 测试文件命名
源文件 greetings.go,测试文件命名为 greetings_test.go,包名与被测代码相同或加 _test 后缀:
package greetings
import "testing"
func TestHello(t *testing.T) {
got := Hello("Go")
want := "Hello, Go"
if got != want {
t.Errorf("got %q, want %q", got, want)
}
}运行测试:
go test
go test -v # 详细输出
go test -cover # 覆盖率
go test -run TestHello # 只跑指定名字
go test ./... # 当前模块所有包2. 表驱动测试 Table-Driven
Go 推荐使用表驱动方式:
func TestAdd(t *testing.T) {
tests := []struct {
a, b, want int
name string
}{
{1, 2, 3, "two positives"},
{0, 0, 0, "zeros"},
{-1, 1, 0, "neg+pos"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
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)
}
})
}
}t.Run 启动子测试,便于独立定位失败:
go test -run TestAdd/two_positives3. 基准测试 Benchmark
文件中 _test.go 后缀,函数名 BenchmarkXxx(b *testing.B):
func BenchmarkSprintf(b *testing.B) {
for i := 0; i < b.N; i++ {
fmt.Sprintf("%d", i)
}
}运行:
go test -bench .
go test -bench . -benchmem # 显示内存分配输出形如 BenchmarkSprintf-8 30000000 40.2 ns/op 16 B/op 1 allocs/op。
4. 示例测试 Example
写在 ExampleXxx 函数中,输出会与 // Output: 注释比对:
func ExampleHello() {
fmt.Println(Hello("Go"))
// Output: Hello, Go
}非常适合做文档示例(pkg.go.dev 自动展示)。
5. TestMain 全局 setup/teardown
func TestMain(m *testing.M) {
setup()
code := m.Run()
teardown()
os.Exit(code)
}6. 失败辅助
t.Error("失败但继续") // 类似 log.Print + continue
t.Errorf("带格式失败")
t.Fatal("立即失败") // 类似 log.Fatal + return
t.Fatalf("立即失败带格式")
t.Skip("跳过该用例")
t.Logf("记录日志")7. httptest 测试 HTTP
func TestHandler(t *testing.T) {
req := httptest.NewRequest("GET", "/users/1", nil)
rec := httptest.NewRecorder()
handler(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("status=%d", rec.Code)
}
}8. testify 等第三方库
可选用的扩展库:
github.com/stretchr/testify:assert、mock、suitegithub.com/DATA-DOG/godog:BDDgithub.com/stretchr/mock:接口 mock
但官方测试包足以覆盖 90% 场景,建议先用标准库。
小结
Go 测试简单、统一、零配置,CI 中只需 go test ./...。配合基准测试、表驱动、子测试,可形成高质量工程能力。下一章学习 Go 1.18 引入的泛型。