本 skill 面向代码已存在、需要补充测试的场景(区别于 TDD 先写测试再写代码)。
通过五个阶段,从项目理解到测试交付,系统性地为已有项目生成高质量测试。
核心理念:
支持两种安装方式,使用 AskQuestion 让用户选择:
项目级安装(推荐协作项目,跟随 git 提交,团队共享):
mkdir -p .cursor/skills && cp -r /path/to/test-gen .cursor/skills/test-gen
# 同时安装斜杠命令
mkdir -p .cursor/commands && cp /path/to/test-gen.md .cursor/commands/test-gen.md
用户级安装(个人全局使用,跨项目可用):
mkdir -p ~/.cursor/skills && cp -r /path/to/test-gen ~/.cursor/skills/test-gen
> 项目级安装的 skill 优先于用户级。如果两处都存在,项目级生效。
> 用户级安装时不支持斜杠命令(命令只能放在项目的 .cursor/commands/ 下)。
在开始前,自动检测项目环境:
检测清单:
1. 语言 — 扫描文件后缀和配置文件(package.json / go.mod / pyproject.toml / pom.xml / Cargo.toml 等)
2. 测试框架 — 检查已有依赖(jest / vitest / pytest / go test / JUnit / RSpec 等)
3. 已有测试 — 搜索 *_test.* / *.test.* / *.spec.* / test_*.* 文件
4. 覆盖率工具 — 检查是否已配置(nyc / c8 / coverage.py / go cover 等)
5. 设计文档 — 搜索 README / ARCHITECTURE / docs/ / DESIGN.md
6. 构建系统 — 检查 Makefile / Taskfile / justfile 是否存在
7. VCS 状态 — 检查 git 是否可用(用于后续变更行覆盖率)
如果检测到项目有测试框架但没有覆盖率工具,自动安装:
| 语言 | 条件 | 安装命令 |
|---|---|---|
| ------ | ------ | ---------- |
| Python | 有 pytest 但无 pytest-cov | pip install pytest-cov |
| JS/TS (Jest) | 无 --coverage 配置 | 已内置,无需安装 |
| JS/TS (Vitest) | 无 @vitest/coverage-* | npm install -D @vitest/coverage-v8 |
| Go | — | 内置 go test -cover,无需安装 |
| Rust | 无 cargo-tarpaulin/llvm-cov | cargo install cargo-llvm-cov |
| Java (Maven) | 无 JaCoCo 插件 | 在 pom.xml 中添加 JaCoCo 插件配置 |
| Java (Gradle) | 无 jacoco 插件 | 在 build.gradle 中添加 id 'jacoco' |
| C#/.NET | 无 coverlet | dotnet add package coverlet.collector |
安装前使用 AskQuestion 确认:
检测到项目缺少覆盖率工具。建议安装:
- [工具名] (安装命令: `xxx`)
是否立即安装?
详细的安装配方见 coverage-recipes.md。
将检测结果汇报给用户,确认无误后进入覆盖率阈值加载。
在前置检测完成后、进入 Phase 1 之前,加载覆盖率阈值。
三层阈值,各自独立判定:
coverage_thresholds:
overall: # 全项目总体阈值
line: 80 # 行覆盖率 %
branch: 70 # 分支覆盖率 %
function: 80 # 函数覆盖率 %
per_file: # 单文件阈值(任何单文件不低于此值)
line: 60
branch: 50
delta: # 变更行覆盖率阈值
line: 90 # 新增/修改的代码行覆盖率
按顺序查找,找到即停:
.test-gen.yaml(项目根目录)```yaml
coverage_thresholds:
overall:
line: 85
branch: 75
function: 85
per_file:
line: 70
branch: 60
delta:
line: 95
```
pyproject.toml:[tool.test-gen.coverage_thresholds]package.json:"test-gen": { "coverage_thresholds": { ... } }加载后向用户展示当前生效的阈值及来源。
overall + per_file 阈值逐项对比delta 阈值对比--fail-under 参数(如工具支持)!! NO 并给出与目标的差距目标:理解项目的设计意图、模块划分和数据流。
格式:
## 项目理解摘要
### 项目类型
[Web 应用 / API 服务 / CLI 工具 / 库 / ...]
### 模块划分
| 模块 | 职责 | 核心文件 |
|------|------|----------|
### 核心数据流
[描述主要数据流向]
### 外部依赖
[列出所有外部依赖及其用途]
### 业务规则与约束
[从文档中提取的关键业务规则]
详细指南见 design-analysis-guide.md。
使用 AskQuestion 向用户展示项目理解摘要,确认:
用户确认后进入 Phase 2。
目标:从公开 API 的角度理解每个函数的契约,不看实现。
禁止读取函数体。只看签名、类型定义、文档注释。
违反这条规则就失去了黑盒视角,测试会不自觉地追随实现细节。
对每个函数,基于签名推断:
| 维度 | 说明 |
|------|------|
| 正常输入 | 典型合法输入 |
| 边界值 | 空值、零值、最大/最小值、空集合 |
| 异常输入 | 类型不匹配、nil/null/undefined、非法格式 |
| 异常路径 | 基于返回的 error/exception 类型推断 |
| 并发安全 | 如果签名暗示并发使用(channel、mutex、sync 相关) |
按模块分组,每个函数一行:
### 模块: [name]
| 函数 | 参数 | 返回值 | 推断的测试维度 |
|------|------|--------|----------------|
| CreateUser | (name string, email string) | (User, error) | 正常/空字符串/重复email/非法格式 |
目标:判断每个函数是否适合直接编写测试,不适合的直接指出设计缺陷。
这是本 skill 的关键差异化阶段。大多数测试生成工具会为所有函数无脑生成测试,
导致大量 mock 堆砌和脆弱测试。本阶段的职责是诚实评估。
详细清单和示例见 testability-checklist.md。
核心维度:
| 维度 | 好的设计 | 坏的设计 |
|---|---|---|
| ------ | ---------- | ---------- |
| 纯度 | 相同输入 → 相同输出 | 依赖全局状态或隐式输入 |
| 依赖注入 | 外部依赖通过参数传入 | 函数内部硬编码 new/connect |
| 接口隔离 | 参数各自独立,职责清晰 | God Object 参数,一个 struct 传所有 |
| 确定性 | 不依赖时间、随机数 | 内部调用 time.Now() 或 Math.random() |
| 可观测性 | 行为可通过返回值验证 | 关键行为只通过副作用发生 |
| 函数粒度 | 单一职责,逻辑聚焦 | 一个函数做五件事 |
对每个函数给出判定:
可测 (TESTABLE)
需重构 (NEEDS_REFACTOR)
跳过 (SKIP)
## 可测试性报告
### 统计
- 可测: X 个函数
- 需重构: Y 个函数
- 跳过: Z 个函数
### 需重构的函数
#### [函数名]
- **问题**: [具体的设计缺陷]
- **影响**: [为什么这会导致测试困难]
- **建议**: [具体的重构方向]
### 跳过的函数
| 函数 | 跳过原因 |
|------|----------|
使用 AskQuestion 展示可测试性报告:
目标:为所有"可测"函数生成高质量单元测试,并产出覆盖率报告。
test_<行为>_when_<条件>_should_<预期>_test. 或 __tests__/ 目录使用项目对应的覆盖率工具(参见 coverage-recipes.md)。
产出摘要(阈值从覆盖率阈值配置中读取):
```markdown
## 覆盖率报告
### 总体覆盖率
| 指标 | 覆盖率 | 目标 | 达标 |
|------|--------|------|------|
| 行覆盖率 | XX.X% | 80% | YES / !! NO (-X.X%) |
| 分支覆盖率 | XX.X% | 70% | YES / !! NO (-X.X%) |
| 函数覆盖率 | XX.X% | 80% | YES / !! NO (-X.X%) |
### 单文件覆盖率(未达标文件)
| 文件 | 行覆盖率 | 目标 | 差距 |
|------|----------|------|------|
| [仅列出低于 per_file 阈值的文件] |
### 未覆盖的关键路径
[列出仍未覆盖的重要代码路径]
```
除全量覆盖率外,计算仅针对本次变更代码的覆盖率。
这对于"给已有项目补测试"的场景尤为重要 — 关注新增测试是否覆盖了目标代码。
步骤:
git diff 获取相对于基准分支(main/master)的变更行详细工具配方见 coverage-recipes.md 中的"变更行覆盖率"章节。
追加到覆盖率报告中(阈值从 delta 配置读取):
```markdown
### 变更行覆盖率(Delta Coverage)
基准分支: main
变更文件数: N
变更行数: M
目标: 90%
| 文件 | 变更行 | 已覆盖 | 覆盖率 | 达标 |
|------|--------|--------|--------|------|
总变更行覆盖率: XX.X% (目标: 90%) — YES / !! NO
```
如果前置检测发现项目有 Makefile,生成测试相关 make targets
(test, test-unit, test-e2e, test-cov, test-cov-delta, test-cov-html)。
生成规则:
COV_LINE ?= 80)使用 AskQuestion 展示:
目标:基于用户故事和数据流,生成端到端集成测试。
基于 Phase 1 的项目理解,识别核心 E2E 场景:
| 项目类型 | 典型路径 |
|----------|----------|
| Web 应用 | 用户注册 → 登录 → 核心操作 → 验证结果 |
| API 服务 | 认证 → CRUD 操作 → 状态验证 → 错误处理 |
| CLI 工具 | 参数解析 → 执行 → stdout/stderr 验证 → 退出码 |
| 库 | 公开 API 的集成调用链 |
| 项目类型 | 推荐框架 |
|----------|----------|
| Web 前端 | Playwright(首选)/ Cypress |
| API 服务 (Node) | supertest |
| API 服务 (Go) | net/http/httptest |
| API 服务 (Python) | httpx + pytest |
| CLI 工具 | 子进程调用 + 断言 stdout/stderr/exit code |
## E2E 测试报告
### 测试场景
| 场景 | 覆盖的用户路径 | 状态 |
|------|----------------|------|
### 测试数据管理
[描述 setup/teardown 策略]
生成的测试代码必须满足:
遵循项目已有惯例。如无惯例:单元测试同目录 _test.,E2E 测试放 e2e/ 目录。
| 情况 | 处理方式 |
|---|---|
| ------ | ---------- |
| 无设计文档 | Phase 1 降级为目录结构分析,明确告知用户信息来源 |
| 无类型信息(纯 JS/Python) | Phase 2 从 doc comment 和变量名推断类型 |
| 函数全部"需重构" | 暂停流程,给用户一份完整的重构建议清单 |
| 测试框架未安装 | 提示安装命令,等用户确认后继续 |
| 覆盖率工具未安装 | 自动检测并提示安装(见前置检测章节),用户确认后安装 |
| 测试发现被测代码 bug | 保留失败测试,在报告中标记为"疑似 bug" |
| 非 git 仓库 | 跳过变更行覆盖率,仅产出全量覆盖率 |
| Makefile 已有同名 target | 跳过该 target,不覆盖已有配置 |
| 无 .test-gen.yaml 配置 | 使用默认阈值(80/70/80、60/50、90),告知用户可创建配置文件自定义 |
| 覆盖率未达标 | 报告中标注差距,不阻塞流程,由用户决定是否补充测试 |
共 1 个版本