测试库不再使用 SQLite 内存库,改为每个测试包独立的 MySQL 库, 表结构由 backend/migrations 的真实迁移建立。 基础设施 - 新增 internal/database/testdb.go:NewTestDB() 保持原签名(28 处调用零改动), 内部连接 MySQL 测试库,首次调用重建库并执行全部真实迁移,之后每次调用清空全表 - 库名 = 测试库基础名 + 包目录名,同包共享、跨包隔离,使 go test ./... 可并发执行 (共用一库时各包会互相 DROP/CREATE 导致 database exists) - 新增 NewDryRunDB():MySQL 方言 + DryRun,供断言 SQL 形态的 5 处测试使用 - 新增 AutoMigrateIfMissing():只对不存在的表 AutoMigrate, 避免改动迁移定义的索引与约束 - 新增 scripts/prepare-test-db.sh:建库与通配授权,可重复执行 - 33 处 sqlite.Open 全部替换,26 个测试文件改用 MySQL - 删除 46 处手写 SQLite 方言 DDL(AUTOINCREMENT / INTEGER PRIMARY KEY / TEXT), 表统一由真实迁移建立 - go.mod 移除 gorm.io/driver/sqlite 与 mattn/go-sqlite3 - 删除生产代码里 distribution/repository.go 的 sqlite 方言分支(strftime) 对齐环境后暴露并修复的真实问题 - DATE(col) 在 MySQL 返回 DATE 类型,使 Date string 字段变成 RFC3339 时间串, 大盘日期会带出时间部分;28 处改为 DATE_FORMAT(col, '%Y-%m-%d') - 系统自动创建分销结算批次时 created_by 写入 0,违反指向 admin_users 的外键, 生产会直接插入失败;改为写 NULL(新增 adminRef helper) - MySQL DATETIME 仅到秒,静默截断 Go 侧写入的时间, 导致租用时长计算与结算时点回读比对偏差; rental_orders 的 9 个业务时间列提到 DATETIME(6) - 测试数据补齐严格模式要求的 NOT NULL 列、修正超出列长的取值、补齐外键父记录 迁移 - 000065 的 ALTER TABLE rental_orders 一并完成业务时间列精度调整, feature 侧不再单开迁移文件 脚本与文档 - check.sh / check-backend-coverage.sh 去掉 -p 1 串行与 SQLite 相关注释 - 更新 docs/Repository层测试补充总结.md 的测试运行说明
9.3 KiB
9.3 KiB
Repository 层测试补充工作总结
完成时间: 2026-06-10
工作内容: 为核心金融模块的 Repository 层补充单元测试和集成测试
✅ 已完成工作
1. Wallet 模块测试补充
1.1 纯逻辑测试 (repository_logic_test.go)
创建了 14 个测试用例,无需数据库,测试核心业务逻辑:
applyEntry 函数测试(余额变更核心逻辑):
- ✅
TestApplyEntryAvailableBalanceIn- 可用余额入账 - ✅
TestApplyEntryAvailableBalanceOut- 可用余额出账 - ✅
TestApplyEntryFrozenBalanceIn- 冻结余额入账 - ✅
TestApplyEntryFrozenBalanceOut- 冻结余额出账 - ✅
TestApplyEntryInsufficientAvailableBalance- 可用余额不足拒绝 - ✅
TestApplyEntryInsufficientFrozenBalance- 冻结余额不足拒绝 - ✅
TestApplyEntryMultipleOperations- 连续多次操作 - ✅
TestApplyEntryEdgeCases- 边界情况(刚好够扣、差1分不够扣)
Entry 验证测试:
- ✅
TestAppendEntriesValidatesEntry- 条目参数验证
已有测试(保持兼容):
- ✅
TestApplyEntryRoundsMoneyBeforeComparing- 金额四舍五入 - ✅
TestApplyEntryKeepsWalletMoneyAtJiaoPrecision- 钱包精度保持 - ✅
TestNewLedgerNoUsesReadableFormat- 流水号格式验证
1.2 集成测试 (repository_integration_test.go)
创建了 9 个集成测试用例,使用 MySQL 测试库:
Account 相关:
- ✅
TestRepositoryAccountCreatesAccountIfNotExists- 账户自动创建
Recharge 相关:
- ✅
TestRepositoryRechargeIncreasesAvailableBalance- 充值增加可用余额 - ✅
TestRepositoryConfirmRechargeFromChannelIsIdempotent- 渠道充值幂等性 - ✅
TestRepositoryConfirmRechargeFromChannelRejectsInvalidParams- 参数验证
AppendEntries 集成测试:
- ✅
TestAppendEntriesUpdatesBalanceCorrectly- 余额计算正确性 - ✅
TestAppendEntriesRejectsInsufficientBalance- 余额不足回滚 - ✅
TestAppendEntriesSkipsZeroAmount- 跳过零金额条目
Ledger 相关:
- ✅
TestRepositoryLedgerPagination- 账本分页查询
2. 测试基础设施
2.1 测试辅助工具 (database/test_helper.go)
创建了数据库测试辅助函数:
func NewTestDB() *gorm.DB // 返回指向 MySQL 测试库的干净连接
func NewDryRunDB() *gorm.DB // MySQL 方言的 DryRun 连接,仅生成 SQL 不执行
特点:
- 测试跑在真实 MySQL 上,表结构由
backend/migrations的真实迁移建立,与生产一致 - 每个测试包使用独立库
hfb_sys_test_<包名>,因此go test ./...可并发执行 - 每次
NewTestDB()调用会清空全部表,测试之间互相隔离 - 需要 MySQL 可用:首次执行
bash scripts/prepare-test-db.sh完成建库授权
📊 测试覆盖率提升
Wallet 模块
| 测试类型 | 文件 | 测试用例数 | 覆盖内容 |
|---|---|---|---|
| Service 层 | service_test.go | 8 | 依赖检查、业务规则 |
| Repository 逻辑 | repository_logic_test.go | 14 | applyEntry 核心逻辑 |
| Repository 集成 | repository_integration_test.go | 9 | 数据库操作完整流程 |
| 原有测试 | repository_test.go | 3 | 金额精度、流水号格式 |
| 总计 | 4 个文件 | 34 个 | 全面覆盖 |
预计覆盖率提升:11.0% → 40%+
🎯 测试策略
1. 分层测试
- Service 层:测试参数验证、依赖检查、业务规则
- Repository 逻辑层:测试纯函数逻辑(applyEntry、ensureAccount 等)
- Repository 集成层:测试数据库事务、并发控制、幂等性
2. 测试覆盖重点
核心业务逻辑
- ✅ 余额变更(可用/冻结余额的增减)
- ✅ 余额不足判断
- ✅ 金额精度处理(角为最小单位)
边界情况
- ✅ 余额刚好够扣
- ✅ 余额差1分不够扣
- ✅ 零金额处理
- ✅ 负数金额处理
并发安全
- ✅ 账户锁定(
FOR UPDATE) - ✅ 事务回滚
- ✅ 幂等性保证
数据一致性
- ✅ 账户余额 = 所有流水累计
- ✅ 流水号唯一性
- ✅ 事务原子性
🔄 待完成工作
Order 模块测试(下一步)
TestRepositoryCreate- 订单创建流程TestRepositoryPay- 支付状态转换TestRepositoryCancel- 取消订单逻辑TestRepositorySubmitHandoff- 交接流程TestRepositoryConfirmReceive- 确认收货TestRepositorySubmitCheckout- 结账计算TestRepositoryAcceptCheckout- 接受结账TestRepositoryCounterCheckout- 反价逻辑- 并发创建订单测试
- 超时场景测试
Payment 模块测试
TestRepositoryStart- 支付单创建与复用TestRepositoryStartRefund- 退款流程TestRepositoryHandleNotify- 支付回调处理TestRepositoryQuery- 支付查询- 支付单幂等性测试
- 退款幂等性测试
- Mock 渠道测试
📝 测试编写规范
1. 命名规范
// Service 层测试
func TestService<Method>With<Condition>(t *testing.T)
// Repository 逻辑测试
func Test<Function><Behavior>(t *testing.T)
// Repository 集成测试
func TestRepository<Method><Scenario>(t *testing.T)
2. 测试结构
func TestXxx(t *testing.T) {
// 1. Setup(如果需要)
db := setupTestDB(t)
defer cleanupTestDB(t, db)
// 2. Given(准备测试数据)
userID := uint64(1001)
amount := int64(10000)
// 3. When(执行操作)
result, err := repo.Method(userID, amount)
// 4. Then(验证结果)
if err != nil {
t.Fatalf("Method() error = %v", err)
}
if result != expected {
t.Fatalf("result = %v, want %v", result, expected)
}
}
3. Table-Driven Tests
func TestXxx(t *testing.T) {
testCases := []struct {
name string
input Input
want Output
wantErr error
}{
{"正常情况", Input{...}, Output{...}, nil},
{"边界情况", Input{...}, Output{...}, nil},
{"异常情况", Input{...}, nil, ErrXxx},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := Method(tc.input)
if err != tc.wantErr {
t.Fatalf("error = %v, want %v", err, tc.wantErr)
}
if got != tc.want {
t.Fatalf("got = %v, want %v", got, tc.want)
}
})
}
}
🚀 如何运行测试
1. 首次运行(需要准备 MySQL 测试库)
# 启动本地 MySQL(如未启动)
bash scripts/dev.sh
# 建库并授权(可重复执行,只需一次)
bash scripts/prepare-test-db.sh
测试库由测试代码自行创建:首次连接时会重建 hfb_sys_test_<包名> 并执行
backend/migrations 下的真实迁移,无需手工建表。
可通过 HFB_TEST_DSN 覆盖测试库连接(库名必须包含 test 或 e2e)。
2. 运行所有 wallet 模块测试
go test ./internal/modules/wallet -v
3. 运行特定测试
# 只运行 Service 层测试
go test ./internal/modules/wallet -run TestService -v
# 只运行逻辑测试
go test ./internal/modules/wallet -run Logic -v
# 只运行集成测试
go test ./internal/modules/wallet -run TestRepository -v
4. 查看覆盖率
go test ./internal/modules/wallet -cover
go test ./internal/modules/wallet -coverprofile=coverage.out
go tool cover -html=coverage.out
💡 测试最佳实践
1. 快速反馈
- 优先运行纯逻辑测试(快速,无依赖)
- 其次运行集成测试(需要数据库)
- 最后运行 E2E 测试(最慢)
2. 测试隔离
- 每个测试使用独立的用户ID
- 使用内存数据库避免测试间干扰
- 测试顺序无关(可并发运行)
3. 可维护性
- 提取公共的测试数据构造函数
- 使用 Table-Driven Tests 减少重复代码
- 清晰的测试命名和注释
4. 边界条件
- 测试零值、负值、边界值
- 测试并发场景
- 测试错误路径
📈 进度总结
| 模块 | Service 测试 | Repository 逻辑测试 | Repository 集成测试 | 状态 |
|---|---|---|---|---|
| wallet | ✅ 8 个 | ✅ 14 个 | ✅ 9 个 | 已完成 |
| order | ✅ 11 个 | ⏳ 待补充 | ⏳ 待补充 | 进行中 |
| payment | ✅ 10 个 | ⏳ 待补充 | ⏳ 待补充 | 进行中 |
当前总计: 29 个 Service 测试 + 14 个逻辑测试 + 9 个集成测试 = 52 个测试用例
目标: 核心模块测试覆盖率达到 60%+
🎉 成果
- ✅ 建立了完整的测试框架和规范
- ✅ 为 wallet 模块补充了 23 个新测试用例
- ✅ 创建了可复用的测试基础设施
- ✅ 提供了清晰的测试编写指南
- ✅ 为后续测试工作打下坚实基础
备注:
- 集成测试跑在真实 MySQL 上,需先执行
bash scripts/prepare-test-db.sh完成建库授权 - 表结构来自
backend/migrations的真实迁移,测试环境与生产一致 - 测试覆盖了核心业务逻辑、边界条件、并发安全和数据一致性
下一步建议:
- 执行
bash scripts/prepare-test-db.sh准备测试库 - 运行 wallet 模块所有测试验证通过
- 继续为 order 和 payment 模块补充 Repository 层测试