测试体系概述
井云服务中心采用分层测试策略,确保代码质量和系统稳定性。本文档介绍项目的测试体系架构和核心概念。
测试架构
测试分层
项目采用以下测试分层策略:
┌───── ────────────────────────────────┐
│ 端到端测试 (E2E Tests) │
│ (Playwright - 前端) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 集成测试 (Integration Tests) │
│ (服务间通信 + 数据库) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 单元测试 (Unit Tests) │
│ (业务逻辑 + Mock 依赖) │
└─────────────────────────────────────┘
测试工具
- 单元测试: testify + go-sqlmock
- 前端测试: Playwright
- 代码覆盖率: go test -cover
- Mock 框架:
- Gateway 服务: 手动函数字段 mock
- 数据库服务: sqlmock + Ent ORM
测试覆盖率要求
| 类型 | 最低要求 | 理想目标 | 说明 |
|---|---|---|---|
| 单元测试 | 90% | 95%+ | 核心业务逻辑必须达到 95%+ |
| 集成测试 | 70% | 85%+ | 服务间通信和数据库操作 |
| 端到端测试 | 50% | 70%+ | 关键用户流程 |
服务测试状态
| 服务 | 测试文件数 | 覆盖率 | 规范符合性 | 风险等级 |
|---|---|---|---|---|
| agent | 16个 | 93.5% | ✅ 完全符合 | 🟢 低 |
| user | 8个 | 良好 | ✅ 完全符合 | 🟡 中 |
| tenant | 7个 | 一般 | ⚠️ 部分符合 | 🟡 中 |
| payment | 5个 | 良好 | ✅ 完全符合 | 🟡 中 |
| auth | 4个 | 良好 | ✅ 完全符合 | 🟡 中 |
| cron | 3个 | 一般 | ⚠️ 部分符合 | 🟡 中 |
| integration | 1个 | 极差 | ❌ 不符合 | 🔴 高 |
| gateway | 5个 | 0.0% | ❌ 不符合 | 🔴 极高 |
测试策略
1. 单元测试策略
目标: 验证单个函数或方法的正确性
原则:
- 快速执行 (毫秒级)
- 无外部依赖
- 100% 隔离
- 覆盖所有分支
示例:
func TestCreateAgentCategory(t *testing.T) {
tests := []struct {
name string
req *agentv1.CreateAgentCategoryRequest
want *agentv1.CreateAgentCategoryReply
wantErr error
}{
{
name: "成功创建分类",
req: &agentv1.CreateAgentCategoryRequest{Name: "测试分类"},
want: &agentv1.CreateAgentCategoryReply{Id: 1},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 测试逻辑
})
}
}
2. 集成测试策略
目标: 验证服务间协作和数据库操作
原则:
- 使用真实的数据库连接 (或 sqlmock)
- 测试完整的事务流程
- 验证数据一致性
示例:
func TestCreateAgentWithDatabase(t *testing.T) {
mockDB, mock, svc := setupServiceTest(t)
defer mockDB.Close()
// Mock SQL 查询
rows := sqlmock.NewRows([]string{"id"}).AddRow(int64(101))
mock.ExpectQuery(`INSERT INTO "agent_categories"`).
WillReturnRows(rows)
resp, err := svc.CreateAgentCategory(context.Background(), req)
require.NoError(t, err)
require.Equal(t, int64(101), resp.Id)
require.NoError(t, mock.ExpectationsWereMet())
}
3. Mock 策略
Gateway 服务 - 函数字段 Mock
type MockAgentCategoryClient struct {
CreateAgentCategoryFunc func(ctx context.Context, in *agentv1.CreateAgentCategoryRequest, opts ...grpc.CallOption) (*agentv1.CreateAgentCategoryReply, error)
}
func (m *MockAgentCategoryClient) CreateAgentCategory(ctx context.Context, in *agentv1.CreateAgentCategoryRequest, opts ...grpc.CallOption) (*agentv1.CreateAgentCategoryReply, error) {
return m.CreateAgentCategoryFunc(ctx, in, opts...)
}
数据库服务 - sqlmock + Ent
func setupServiceTest(t *testing.T) (*sql.DB, sqlmock.Sqlmock, *YourService) {
mockDB, mock, err := sqlmock.New()
require.NoError(t, err)
drv := entsql.OpenDB("postgres", mockDB)
entClient := entclient.NewClient(entclient.Driver(drv))
// 初始化服务
svc := NewYourService(entClient, logger)
return mockDB, mock, svc
}
测试规范
核心原则
- 字段验证完整性: 返回的结构体所有字段都必须验证
- 错误场景覆盖: 必须测试所有可能的错误情况
- 边界条件测试: 测试空值、零值、最大值等边界条件
- 并发安全测试: 对于并发操作,必须测试并发安全性
完整字段验证示例
// ❌ 错误 - 字段验证不完整
assert.Equal(t, int64(1), resp.Category.Id)
assert.Equal(t, "测试分类", resp.Category.Name)
// ✅ 正确 - 验证所有字段
assert.Equal(t, int64(1), resp.Category.Id)
assert.Equal(t, int64(1), resp.Category.TenantId)
assert.Equal(t, "测试分类", resp.Category.Name)
assert.Equal(t, "测试描述", resp.Category.Description)
assert.Equal(t, int32(1), resp.Category.SortOrder)
assert.False(t, resp.Category.IsFeatured)
assert.True(t, resp.Category.IsActive)
assert.Equal(t, int64(1672531200), resp.Category.CreatedAt)
assert.Equal(t, int64(1672531200), resp.Category.UpdatedAt)
测试执行
运行单个服务测试
cd services/agent
go test ./internal/service/... -v
生成覆盖率报告
go test ./internal/service/... -coverprofile=coverage.out
go tool cover -html=coverage.out -o coverage.html