mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 22:06:38 +08:00
3.9 KiB
3.9 KiB
Go 测试:集成和高级模式
TestMain、验收测试和真实传输层测试的详细参考。 来源:Google Go Style Guide(最佳实践)。
TestMain
来源:Google Go Style Guide(最佳实践)
当 包中的所有测试 都需要共同的设置且需要清理时(例如,共享数据库),使用 func TestMain(m *testing.M)。这 不应该是你的首选 — 尽可能优先使用作用域测试辅助函数或 t.Cleanup。
var db *sql.DB
func TestInsert(t *testing.T) { /* 使用 db */ }
func TestSelect(t *testing.T) { /* 使用 db */ }
func runMain(ctx context.Context, m *testing.M) (code int, err error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
d, err := setupDatabase(ctx)
if err != nil {
return 0, err
}
defer d.Close()
db = d
return m.Run(), nil
}
func TestMain(m *testing.M) {
code, err := runMain(context.Background(), m)
if err != nil {
log.Fatal(err)
}
// defer 语句在 os.Exit 之后不会执行
os.Exit(code)
}
关键点:
- 将设置提取到辅助函数(
runMain)中,使defer能正确工作 - 通过
log.Fatal将失败信息写入 stderr - 确保各个测试用例保持独立 — 重置它们修改的任何全局状态
验收测试
来源:Google Go Style Guide(最佳实践)
验收测试验证实现是否遵循契约,将其视为黑盒。当用户实现你的接口并且你想提供可重用的验证套件时,这种模式很有用。
结构
- 创建测试辅助包(例如,为
chess包创建chesstest) - 导出一个接受被测实现的验证函数:
// Package chesstest 为 chess.Player 实现提供验收测试。
package chesstest
// ExercisePlayer 在单回合中测试 Player 实现。
// 如果玩家走了正确的一步,返回 nil,否则返回描述违规行为的错误。
func ExercisePlayer(b *chess.Board, p chess.Player) error {
move := p.Move()
if putsOwnKingIntoCheck(b, move) {
return &IllegalMoveError{Move: move, Reason: "puts own king in check"}
}
return nil
}
- 最终用户针对验证函数编写简单测试:
func TestAcceptance(t *testing.T) {
player := deepblue.New()
if err := chesstest.ExerciseGame(t, chesstest.SimpleGame, player); err != nil {
t.Errorf("Deep Blue player failed acceptance test: %v", err)
}
}
仅在设置失败时使用 t.Fatal — 验证错误应该返回,而非 fatal。
使用真实传输层
来源:Google Go Style Guide(最佳实践)
在测试基于 HTTP 或 RPC 的组件集成时,优先使用真实传输层往返而非手动实现的客户端 mock:
func TestAPIIntegration(t *testing.T) {
// 使用假后端启动测试服务器
srv := httptest.NewServer(newFakeHandler())
t.Cleanup(srv.Close)
// 对测试服务器使用真实 HTTP 客户端
client := api.NewClient(srv.URL)
result, err := client.GetUser(context.Background(), "user-123")
if err != nil {
t.Fatalf("GetUser() error: %v", err)
}
if result.Name != "Test User" {
t.Errorf("GetUser().Name = %q, want %q", result.Name, "Test User")
}
}
使用生产客户端配合测试服务器,可以确保测试尽可能多地覆盖真实代码,避免模拟客户端行为的复杂性。
常见错误
在 TestMain 中直接调用 os.Exit
os.Exit 立即终止进程 — defer 的清理函数永远不会执行。将设置/清理提取到辅助函数中,使 defer 能正确工作:
// 不好:defer 不会执行
func TestMain(m *testing.M) {
setup()
defer cleanup()
os.Exit(m.Run()) // cleanup() 永远不会执行
}
// 好:提取到辅助函数中,使 defer 在 os.Exit 之前执行
func runTests(m *testing.M) int {
setup()
defer cleanup()
return m.Run()
}
func TestMain(m *testing.M) {
os.Exit(runTests(m))
}