- 新增 templates/go-integration-test.md 集成测试代码骨架模板 - SKILL.md 增加 TG2 集成测试检测:跨 handlers/middleware/routes 变更自动触发 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4.6 KiB
4.6 KiB
Go 集成测试模板
适用场景
当 git diff 显示同一功能的 handlers/ + middleware/ + routes/ 文件均有变更时,除单元测试外应额外生成集成测试。
适用于:
- 多步骤 API 流程(登录→操作→验证→退出)
- 中间件拦截验证(权限、限流、模拟状态限制)
- 租户隔离 / 数据隔离验证
- 跨模块交互(handler ↔ middleware ↔ repository)
检测规则
通过命名模式识别同一功能的跨文件变更:
handlers/{feature}_handler.go
middleware/{feature}_middleware.go
routes/{feature}_routes.go
例如 impersonation_handler.go + impersonation_middleware.go + impersonation_routes.go 同时变更 → 生成 tests/impersonation_integration_test.go。
生成规则
- 测试文件放在
backend/tests/目录(与test_helpers.go同包) - 使用
SetupTestApp+defer TeardownTestApp初始化真实路由和数据库 - 每个测试完全自包含:独立创建/清理数据
- 异步持久化操作(goroutine 写 DB)需
time.Sleep(200-500ms)后再验证
代码骨架
package tests
import (
"encoding/json"
"fmt"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFeature_Scenario(t *testing.T) {
testApp := SetupTestApp(t)
defer TeardownTestApp(t, testApp)
// === Setup: 创建测试数据 ===
// 使用 test_helpers.go 中的 helper 函数
sysAdmin := CreateTestSystemUser(t, testApp, "admin_scenario")
// tenantID := CreateTestTenant(t, testApp, "scenario_tenant")
// enterprise := CreateTestEnterpriseWithTenant(t, testApp, "Corp", "CODE", tenantID)
// tenantAdmin := CreateTestTenantAdmin(t, testApp, "tadmin", enterprise.ID, tenantID)
// === Cleanup: 按外键约束顺序清理 ===
defer CleanupImpersonationTestData(t, testApp,
[]int{sysAdmin.ID},
[]int{}, // enterpriseIDs
[]int64{}, // tenantIDs
)
// === 状态变量:跨步骤传递 ===
var token string
t.Run("Step1_InitialAction", func(t *testing.T) {
w := MakeAuthenticatedRequest(t, testApp, http.MethodPost,
"/api/v1/...",
map[string]string{"key": "value"},
sysAdmin,
)
require.Equal(t, http.StatusOK, w.Code, "Step1 failed: %s", w.Body.String())
var resp map[string]interface{}
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
// 提取后续步骤需要的数据
token = resp["data"].(map[string]interface{})["token"].(string)
})
t.Run("Step2_VerifyState", func(t *testing.T) {
if token == "" {
t.Skip("Step1 failed")
}
// 使用 Step1 产出的 token 继续验证
})
t.Run("StepN_AsyncVerification", func(t *testing.T) {
// 异步写入的数据需要等待
time.Sleep(300 * time.Millisecond)
// 然后验证 DB 数据
})
}
4 种必测场景
| 场景类型 | 说明 | 示例 |
|---|---|---|
| Happy Path | 完整正常流程 | 开始模拟 → 访问 API → 查状态 → 退出 → 确认恢复 |
| 权限拒绝 | 无权限用户尝试操作 | 普通用户尝试模拟 → 403 |
| 隔离验证 | 跨租户/跨企业数据隔离 | tenant2 admin 访问 tenant1 企业 → 403 |
| 边界条件 | 输入校验、状态冲突 | 原因太短 → 400;已在模拟中再次模拟 → 403 |
Helper 函数参考
// 来自 test_helpers.go
SetupTestApp(t) // 初始化完整应用(路由 + DB)
CreateTestSystemUser(t, app, username) // 系统管理员 + JWT
CreateTestTenant(t, app, name) // 创建租户,返回 int64 ID
CreateTestEnterpriseWithTenant(t, app, name, code, tenantID) // 带租户的企业
CreateTestTenantAdmin(t, app, username, enterpriseID, tenantID...) // 租户管理员
CreateTestEnterpriseUser(t, app, username, enterpriseID) // 普通企业用户
ExtractImpersonationToken(t, response) // 从模拟响应提取 token
MakeAuthenticatedRequest(t, app, method, path, body, user) // 发送认证请求
CleanupImpersonationTestData(t, app, userIDs, enterpriseIDs, tenantIDs) // 按FK顺序清理
注意事项
- Token 链式传递:模拟 API 返回新 token,后续请求必须用新 token
- 异步持久化:handler 用 goroutine 写 session/audit,测试需 Sleep 后再查
- 数据隔离:每个测试用唯一的 username/code,避免测试间干扰
- 清理顺序:外键约束要求先删子表再删父表