Files
OpenFlare/.agents/skills/go-testing/references/INTEGRATION.md
T
2026-08-04 11:40:32 +08:00

145 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Go 测试:集成和高级模式
TestMain、验收测试和真实传输层测试的详细参考。
来源:Google Go Style Guide(最佳实践)。
---
## TestMain
> **来源**:Google Go Style Guide(最佳实践)
当 **包中的所有测试** 都需要共同的设置且需要清理时(例如,共享数据库),使用 `func TestMain(m *testing.M)`。这 **不应该是你的首选** — 尽可能优先使用作用域测试辅助函数或 `t.Cleanup`。
```go
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(最佳实践)
验收测试验证实现是否遵循契约,将其视为黑盒。当用户实现你的接口并且你想提供可重用的验证套件时,这种模式很有用。
### 结构
1. 创建测试辅助包(例如,为 `chess` 包创建 `chesstest`)
2. 导出一个接受被测实现的验证函数:
```go
// 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
}
```
3. 最终用户针对验证函数编写简单测试:
```go
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:
```go
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` 能正确工作:
```go
// 不好: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))
}
```