Skip to content

feat: 完善后端测试体系,围绕核心业务链路补充有效测试,写对 vibe coding 有效的测试 CI #367

Description

@agermel

背景

目前仓库虽然已经存在一定数量的测试,但是现有的测试基本基本主要集中在参数校验、简单错误判断等边角逻辑上,缺少对真实业务流程和关键行为的验证。这部分测试基本就是典型的 Vibe Coding 副产物:AI 为了完成 feature 和让 CI 变绿,机械地堆了一批测试文件。测试数量看起来不少,但真正验证核心行为的内容很有限,大量 case 属于低价值甚至无价值的噪音,后续只会变成维护负担,无法有效验证核心功能是否正确。

当前测试主要存在以下问题:

  • 现有测试整体偏向局部函数和辅助逻辑,对核心业务流程和主要使用场景的覆盖还不够充分;
  • 部分测试主要验证是否返回错误,但对具体错误类型、业务状态和最终返回结果的验证不够完整;
  • 从 BFF 到底层服务的完整调用链路测试基本缺失,登录、认证、缓存、降级、重试、数据同步等关键业务分支尚未得到端到端验证;
  • 测试场景与实际业务流程结合不够紧密,难以充分反映用户的真实使用路径;
  • 部分测试更关注代码是否被执行,而不是业务行为是否符合预期,对防止业务回归的帮助有限;
  • 跨模块、跨服务调用过程中的错误传递、状态变化和最终业务结果缺少完整验证。

这会导致一个问题:

CI 可以全部通过,但关键业务行为发生回归时仍然无法及时发现。

我们需要验证整体服务链路的测试,核心目的是让 Agent 在开发和修改代码时能够及时发现问题,避免影响真实业务逻辑的正常运行,并通过测试提醒 Agent 判断其新增或修改的逻辑是否破坏了原有功能

例如近期“统一身份认证账号未初始化错误进入本地登录 fallback”的问题,就是一个典型的核心业务分支缺少测试保护的场景。

目标

建立一套能够真正覆盖核心业务链路、验证关键异常分支并防止后续开发导致原有逻辑出现问题的测试体系

测试优先保证以下内容:

  1. 核心业务流程正确;
  2. 关键异常分支行为正确;
  3. fallback / retry / cache 等降级行为符合预期;
  4. 跨服务错误能够正确传播和映射;
  5. 修复过的线上 Bug 有对应回归测试;
  6. CI 中的测试失败应当能够反映真实的业务行为变化,避免测试与无关的实现细节或日志文案绑定。

例如,服务中的日志从:

fmt.Println("login failed")

修改为:

fmt.Println("user login failed")

不应导致测试失败。测试应关注登录失败这一业务行为及其对应的错误处理结果,而不是具体的日志文本。

建议测试分层

1. 单元测试

主要覆盖无外部依赖或可以轻量 mock 的核心逻辑。

例如:

  • 业务输入与参数校验;
  • 核心业务流程;
  • 业务状态与状态转换;
  • 数据查询、保存、更新、删除;
  • 数据处理与结果转换;
  • 外部服务调用及返回结果处理;
  • 错误处理、错误传播与错误映射;
  • 缓存、重试、降级等非正常流程;
  • 权限、认证与登录状态;
  • 不同边界条件和异常场景。

优先采用 table-driven test,可以参考 Kratos 源码中的测试实践。

单元测试应直接放在被测试代码所在目录,与源文件对应。

例如:

be-ccnu/crawler/
├── passport.go
├── passport_test.go
├── undergrad.go
└── undergrad_test.go

2. Service 业务测试

重点测试每个 service 的主要业务方法,而不仅是 helper。

通过 fake / mock DAO、RPC Client、Cache 等依赖,覆盖完整业务分支。

例如 be-user.Check 至少需要覆盖:

  • CCNU 认证成功;
  • 账号密码错误;
  • 账号未初始化;
  • CCNU 临时异常 + 本地密码正确;
  • CCNU 临时异常 + 本地密码错误;
  • CCNU 临时异常 + 本地用户不存在。

其中需要明确验证:

哪些错误允许 fallback,哪些错误必须直接返回。

测试文件放在对应 service 目录。

例如 be-user

be-user/service/
├── user.go
├── user_check_test.go
├── user_save_test.go
└── user_cookie_test.go

建议按照业务能力拆分,而不是全部塞进一个巨大的 user_test.go

3. 跨服务错误传播测试

对于关键业务错误,需要验证完整传播链路,而不是只测试最后一层 mapping。

例如:

crawler
    ↓
be-ccnu
    ↓
be-user / be-classlist / be-grade
    ↓
BFF

需要保证错误经过 RPC 和多层包装之后,最终仍然能够被正确识别并映射为对应业务错误。

建议新增统一目录:

tests/integration/

例如:

tests/integration/
├── login_flow_test.go
├── class_refresh_flow_test.go
├── grade_refresh_flow_test.go
└── error_propagation_test.go

这里不要按照代码 package 划分,而按照真实业务链路划分。

4. Bug Regression Test

以后修复影响核心业务的 Bug 时,原则上同时增加对应的回归测试。

测试应能够满足:

修复前失败
修复后通过

防止相同问题再次出现。

原则上:

Bug 属于哪个模块,就放在哪个模块附近。

例如这次:

账号未初始化
→ 被当成 CCNU 服务异常
→ 进入本地密码 fallback
→ 登录成功

这个 Bug 的核心发生在:

be-user/service/user.go

因此可以放:

be-user/service/user_check_test.go

增加:

func TestCheckAccountInitializationDoesNotFallback(t *testing.T)

如果后续某个模块积累了多个重要回归 case,也可以单独拆:

be-user/service/
├── user_check_test.go
└── user_check_regression_test.go

测试编写要求

建议统一以下基本原则:

  • 测试必须明确验证预期行为;
  • 不应只判断 err != nil,需要尽量验证具体错误类型 / reason;
  • 不应使用 t.Logf 代替结果断言;
  • 测试名称应和实际测试的方法、场景一致;
  • mock/fake 应尽量用于构造业务场景,而不是为了让测试通过;
  • 优先测试公开业务行为,而不是大量绑定内部实现细节;
  • 对 retry、fallback、缓存、异步刷新等逻辑,应明确验证是否发生以及发生次数。

例如:

got, err := crypto.Decrypt(ciphertext)
if err != nil {
    t.Fatal(err)
}

if got != want {
    t.Fatalf("Decrypt() = %q, want %q", got, want)
}

而不是只:

t.Logf("plaintext: %s", got)

推进方式

不建议一次性重写全部测试,可以按服务逐步推进。

第一阶段优先覆盖高风险核心链路:

  • be-user

    • 登录认证
    • fallback
    • Save / Delete
  • be-ccnu

    • Passport 登录
    • Cookie 获取
    • 密码错误 / 未初始化 / 服务异常分类
  • be-grade

    • 缓存命中 / miss
    • refresh
    • Cookie 失效
    • fallback / 错误传播
  • be-classlist

    • 获取课表
    • refresh
    • crawler authentication
    • Add / Update / Delete 主要业务分支

第二阶段再补:

  • repository / cache;
  • crawler 解析;
  • BFF 错误映射;
  • 其他低风险服务。

验收标准

第一阶段完成后,希望至少达到:

  • 核心 service 的主要业务方法均存在测试;
  • fallback / retry 等关键分支有明确测试;
  • 关键业务错误存在跨层传播测试;
  • 已修复的重要 Bug 有 regression test;
  • 新增核心业务逻辑时原则上同步增加测试;
  • CI 测试能够有效阻止关键业务行为回归。

覆盖率可以作为辅助指标,但不建议将单纯提高 coverage percentage 作为主要目标。

核心目标应该是:

让测试能够发现真实业务问题,而不是仅仅让 CI 通过,ai 写的测试也要写到点子上啊

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions