💻 源码
# 03 · 测试标准

> **目标**:定义“什么叫测够了”。开发自测是进入 CI 前的第一道防线。

---

## 1. 测试分层

```text
        /\        E2E 测试:少、慢、贵,覆盖真实用户路径
       /  \
      /----\      集成测试:测试模块/服务边界
     /------\
    /        \    单元测试:多、快、稳定,开发者必写
   /__________\
```

- 单元测试优先覆盖核心逻辑。
- 集成测试覆盖接口、数据库、外部服务边界。
- E2E 测试只覆盖关键路径,不要用它替代单元测试。

## 2. 用例写法

推荐 AAA / Given-When-Then:

```python
def test_health_returns_ok():
    # Arrange
    client = app.test_client()

    # Act
    resp = client.get("/health")

    # Assert
    assert resp.status_code == 200
    assert resp.get_json()["status"] == "ok"
```

## 3. 好测试的 5 条铁律

1. 一个用例只测一件事。
2. 覆盖正常值、边界值、异常值。
3. 可重复、无外部网络依赖;有随机性必须固定种子。
4. 测试名能表达场景和预期。
5. 必须有真实断言,禁止 `assert result is not None` 凑数。

## 4. 覆盖率

- 默认建议核心代码覆盖率 ≥ 80%。
- 覆盖率不是目的,关键分支必须被覆盖。
- 如果项目不适合覆盖率门槛,必须在 `00-project-context.md` 说明原因。

## 5. 项目特有质量门禁

不同项目可追加不同质量门禁:

| 项目类型 | 可选门禁示例 |
|---|---|
| Web/API | 健康检查、接口状态码、契约测试 |
| 数据/ML | 数据校验、模型指标(AUC/F1/RMSE 等) |
| 前端 | 组件测试、构建产物检查、可访问性检查 |
| CLI/脚本 | 命令退出码、输入输出快照 |

> 项目特有门禁写进 `00-project-context.md` 第 4 节,再写入 CI。

## 6. flaky test 纪律

- 偶发失败必须根治,不能靠“重跑一次”掩盖。
- 典型原因:时间依赖、随机数、测试顺序依赖、外部网络、共享状态未清理。

## 7. 本地测试由谁执行

默认规则:

| 环境 | 谁执行本地测试 | 怎么做 |
|---|---|---|
| AI 工具有终端权限(Cursor / Claude Code 等) | **AI 执行** | AI 自己运行格式检查、lint、pytest、构建等命令,读取日志,修复问题,把结果写回 `PROGRESS.md` |
| AI 没有终端权限(纯聊天网页) | **学生执行** | 学生复制 AI 给出的命令到终端运行,把完整日志贴回 AI |
| 涉及账号/权限/审批/Secrets | **学生确认** | AI 可指导,但不能代替人输入密码、私钥、审批生产发布 |

AI 能执行命令时,不能只告诉学生“你去运行 pytest”。正确做法是:

```text
AI 写代码 → AI 运行本地自检 → AI 根据日志修复 → AI 再运行 → 全绿后提交/PR → AI 记录结果
```

每次本地自检至少记录:

- 执行的命令。
- 是否通过。
- 失败时的关键错误。
- 修复方式。
- 最终通过结果。