test-backend/.claude/testing.md

4.1 KiB
Raw Permalink Blame History

07 — 测试与错误处理规则

适用所有后端开发任务。AI 最容易忽略的两件事:写测试、统一错误码。


测试规则

必须写测试的场景

场景 测试类型 要求
新增 Service 方法 单元测试 必须
新增 Controller 接口 集成测试 必须
涉及金额计算 单元测试 必须(多组边界值)
涉及状态流转 单元测试 必须(覆盖所有状态路径)
涉及外部接口调用 集成测试 必须Mock 外部依赖)
修 Bug 回归测试 必须(防止复现)
简单 CRUD 可选 不强制

AI 测试规则

AI 完成任务后,必须同时交付测试代码。 禁止说"功能写好了但测试你自己补"。

// ✅ AI 必须产出完整测试
@SpringBootTest
class TaskServiceTest {
    @Autowired
    private TaskService taskService;

    @Test
    void shouldCreateTask() { ... }
    
    @Test
    void shouldNotCreateTaskWithoutTitle() { ... }
    
    @Test
    void shouldCalculateRewardCorrectly() { ... }
}

// ❌ AI 不能这样
// "功能已完成,测试代码未编写,建议后续补充"

测试命名

方法名_场景_预期结果

shouldCreateTask_whenValidInput
shouldNotCreateTask_whenTitleIsEmpty
shouldCalculateReward_whenDiscountApplied
shouldReturnFail_whenTaskNotFound

禁止

  • 测试只测正常路径,不测异常路径
  • 测试依赖执行顺序(每个测试必须独立)
  • 测试里有 Thread.sleep()(用 Awaitility
  • 测试代码复制粘贴业务代码(测了个寂寞)

错误处理规则

返回格式(不变式)

// ✅ 统一返回
Result.ok(data)              // 成功
Result.error("原因")          // 业务失败(用户可见)
Result.error("系统错误")     // 系统异常(用户不可见,只记录日志)

// JSON 格式
{ "code": 200, "message": "success", "data": {...} }      // 成功
{ "code": 500, "message": "任务不存在", "data": null }     // 业务失败
{ "code": 500, "message": "系统繁忙,请稍后重试", "data": null }  // 系统异常

什么情况返回什么

// 查不到 → 业务失败
if (task == null) return Result.error("任务不存在");

// 参数不合法 → 业务失败
if (page < 1) return Result.error("页码必须大于0");

// 没权限 → 业务失败
if (!hasPermission) return Result.error("无权操作");

// 数据库连接失败 → 系统异常(全局异常处理器统一捕获)
// 第三方接口超时 → 系统异常(记录日志 + 返回通用错误)
// 不需要手动 try-catch 每个地方,用全局异常处理器统一处理

全局异常处理器

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    public Result<?> handleBusiness(BusinessException e) {
        log.warn("业务异常: {}", e.getMessage());
        return Result.error(e.getMessage());  // 用户可见
    }

    @ExceptionHandler(Exception.class)
    public Result<?> handleException(Exception e) {
        log.error("系统异常", e);
        return Result.error("系统繁忙,请稍后重试");  // 用户不可见原文
    }
}

禁止

return Result.error(e.getMessage()) 把栈打印给用户 return Result.error("任务不存在")
每个方法都 try-catch 一遍 全局异常处理器统一处理
异常被吞掉 catch(Exception e) {} 至少打日志 log.error("xxx", e)
return null 让前端爆 NPE return Result.error("原因")
成功和失败都用 code=200 失败用 code=500

检查表

测试

  • 新增的非 CRUD 方法有测试
  • 测试覆盖了异常路径(不是只测正常路径)
  • 金额计算有边界值测试
  • AI 交付包含测试代码

错误处理

  • 错误信息用户能看懂(不是技术术语)
  • 系统异常不暴露内部信息给用户
  • 查不到/null 有明确 fail 返回
  • 没有空 catch 块
  • 没有 return null 代替错误返回