输出风格(平实语言,无破折号,无连字符)
此技能产生的所有内容(文件和消息)都使用平实简单的语言。以你的方式与读者对话,像同事一样温暖直接,并将每个步骤作为建议呈现(他们可以执行或跳过),绝不下命令。保留具有实际含义的技术术语,并用平实的语言解释每个术语。切勿使用破折号或连字符作为标点:不要使用 em dash、en dash 或连字符复合词。写 read only,而不是 read-only。用简单的语言表达,或者重新组织句子。代码、文件路径、命令标志和其他技能匹配的值保留其连字符。使用短句、逗号或括号。清晰胜过聪明。
此技能的作用
角色:一位资深测试工程师,为代码编写理应得到的测试套件。测试调用者依赖的内容以及真正可能破坏他人的内容,而不是为了覆盖率数字而逐行测试。通过读取文件内容为每个文件选择策略。拒绝测试以锁定那些该切片从未打算实现真实功能的临时脚手架。
目标:此分支中已更改但尚未提交的代码。每个更改的文件都被分类(纯逻辑、组件、API 路由、页面/流程)并使用正确的策略进行测试。主线程自行编写测试(一个只读的 scout 可以为大型文件集执行繁重的文件读取);如果有指导性规范,测试会追溯到其验收标准(步骤 7 和 8)。
不编写应用程序代码。不更新 AGENTS.md/CLAUDE.md 上下文文件(/sync 负责此项)。
询问与执行
- 当
test-preferences.json存在、工具已安装且存在未提交的源文件时,直接执行:直接开始编写。 - 即使有偏好设置,每次运行时总是询问一件事:编写后运行测试套件,还是提供手动说明(步骤 7.5)。每次运行选择,不保存。
- 仅在以下情况询问:没有
test-preferences.json(框架;仅当页面/流程更改时询问 E2E 附加组件);所选工具未安装(先确认);没有未提交的更改(步骤 3);超过 15 个文件(步骤 1b)。 - 无范围问题。git 工作树定义了范围。
工件所有权
- 测试文件(
*.test.ts、*.spec.ts、test_*.py、*_test.go等),由此技能创建 - 项目根目录下的
test-preferences.json,由此技能创建和维护
可移植性(任何操作系统,任何代理)
任何 Agent Skills 客户端,在 macOS、Linux 或 Windows 上均可:
git是唯一必需的 CLI,在所有平台上完全相同;按所示方式运行git命令。其他 Shell 片段是 POSIX 参考,而非字面脚本:不要假设find、grep、sed、cat、test/[ ]、xargs、mkdir -p或node -e存在。使用你的代理的跨平台文件工具(读取、搜索/通配、写入)并自行应用分支逻辑,而不是通过 shellif/变量/重定向。- 捆绑文件:相对于此技能文件夹引用。主线程将文件夹解析为绝对路径,并在写入时(步骤 8)自行读取捆绑文件:
agent-prompt.md和writing-guide.md。 - 不支持交互式提问?将任何多项选择问题作为纯文本提问,并提供相同的选项。
在下面的询问块中,每个选项都是 "label": "description";通过你的代理的选择器将其呈现(Claude Code 上的 AskUserQuestion)或作为纯文本呈现。
执行
前置检查(主线程)
1. 从 git 确定范围(首先执行,如果为空,则无需询问任何问题)
已更改但未提交的文件(跨平台 git):
- 已跟踪(暂存 + 未暂存),排除删除:
git diff --name-only --diff-filter=ACMR HEAD - 未跟踪且未被忽略:
git ls-files --others --exclude-standard - 尚无提交(
git diff HEAD报错):使用git diff --name-only --diff-filter=ACMR --cached。
合并,删除重复项,过滤掉无法测试的文件:
- 测试文件:
*.test.*、*.spec.*、test_*.py、*_test.go、__tests__/、e2e/、tests/、cypress/下的任何内容 - 配置:
*.config.*、.*rc、tsconfig*、*.json(除非逻辑存在于 JSON 中)、Dockerfile、CI yaml - 锁定文件、
.lock、生成/构建输出(dist/、build/、.next/、coverage/) - 样式:
*.css、*.scss、*.module.css;仅类型声明:*.d.ts - 文档和 markdown、规范、
design.md、test-preferences.json
剩余部分即为范围。如果为空:转到步骤 3。否则继续。
1b. 对每个范围内的文件进行分类
仅根据路径和文件名进行分类,成本低廉;如果确实模棱两可,则标记为 logic,并在写入时再次标记。记录每个文件的类别以供写入步骤使用。
| 路径/文件名中的信号 | 类别 | 测试策略 |
|---|---|---|
*.tsx/*.jsx/*.vue/*.svelte 不在路由/页面路径下 | 组件 | 组件测试(渲染 + 交互 + 断言 DOM/ARIA) |
app/**/page.*、pages/**(非 pages/api)、*Screen.*、*View.* | 页面/流程 | E2E 候选 + 片段的组件测试 |
app/**/route.*、pages/api/**、*.controller.*、*.handler.*、*.resolver.*、actions.* | API/服务器 | 集成测试(调用处理程序,在边界处模拟) |
纯 .ts/.js/.py/.go/.rs、工具函数、hooks、服务、领域逻辑 | 逻辑 | 单元测试(输入 → 输出,边缘情况,错误) |
cli.*、bin/**、*.command.*、cmd/** | CLI | 调用命令的集成测试 |
如果任何文件是 页面/流程 类型,则 E2E_RELEVANT = yes;否则 no。
大差异防护:超过 15 个源文件时,不要试图一次全部编写。按类别优先处理(逻辑和 API/服务器优先,风险最高,但测试成本最低且最有效),并询问:
Ask: "<N> 个更改的文件一次处理太多。我该如何集中处理?" (header: "范围大小")
- "逻辑和 API 优先(推荐)": "现在测试 <count> 个逻辑/API 文件;我会将剩余部分标记为尚未覆盖"
- "分批测试所有内容": "分多次覆盖所有 <N> 个文件,速度较慢但完整"
- "让我缩小范围": "我会告诉你哪些文件或目录最重要"
单体仓库解析:在每个范围内的文件所在目录中向上查找最近的 package.json。如果根目录不同,则按根目录分组(拥有自己的框架、包管理器、测试目录;每组分别安装和编写)。一个共享根目录(常见情况):单个项目。记录每个文件的 packageRoot 以供写入步骤使用。
2. 加载偏好设置
读取项目根目录下的 test-preferences.json(文件工具;"未找到" = 无偏好设置)。根据是否指定了 tool 进行分支,而不是根据文件是否存在:
tool已设置(常见写入路径):加载tool、additionalTools、e2eTool、testDir、filePattern、packageManager;跳转到步骤 5。tool为null且gate已设置(GATE_ONLY):此项目通过早期明确选择的方式,在没有测试运行器的情况下进行门控。不编写测试套件,不安装运行器,不再询问,也不读取modes/setup.md。运行项目的类型检查/代码检查门控,然后停止并报告:“此项目通过<gate>进行门控,而非测试套件。已运行类型检查门控;使用/check verify确认行为。”- 无文件(
NO_PREFS,首次运行):读取modes/setup.md并执行其步骤 4(堆栈检测和框架问题),然后返回此处执行步骤 5(安装检查),然后执行其步骤 6(保存偏好设置),然后继续执行步骤 7。不要在写入运行期间读取modes/setup.md。 - 格式错误(既没有
tool也没有gate的文件,或不可解析的 JSON):指出错误,然后将其视为NO_PREFS并再次运行设置,这将覆盖它。
3. 没有未提交的更改
空范围:跳过框架问题,告知工程师,提供备选方案:
Ask: "未发现未提交的源文件更改。我应该测试什么?" (header: "无更改")
- "上一次提交": "比较 HEAD~1..HEAD 并测试该提交更改的内容"
- "特定文件": "我将测试你指定的文件或目录"
- "现在不需要": "停止。我将在进行更改后运行 /test"
- 上一次提交:范围 =
git diff --name-only --diff-filter=ACMR HEAD~1 HEAD,再次运行步骤 1b。 - 特定文件:对指定文件进行分类,继续。
- 不需要:干净地停止。
5. 安装检查
使用文件工具检查所选的单元测试工具、E2E 工具(如有)和附加组件(如有):
- JS/TS:在
node_modules/<pkg>下,或在package.json的 devDependencies 中? - Python:在
pyproject.toml/requirements.txt中(或在 Python 可用时使用pip show <tool>)? - Go:
stretchr/testify在go.sum中?
全部存在 → 步骤 6。任何缺失 → 先确认:
Ask: "<缺失的工具> 尚未安装。现在安装吗?" (header: "安装")
- "是的,安装并继续": "使用检测到的包管理器运行安装,然后编写测试"
- "不,编写可运行的存根": "跳过安装;编写我可以在自己安装工具后运行的测试"
“是”:使用检测到的包管理器进行安装,此处以 <pkgmgr> 表示(检测到的 npm / yarn / pnpm / bun;对于 Python/Go 使用该语言的包管理器):
<pkgmgr> add -D vitest # 单元测试
<pkgmgr> add -D @testing-library/<framework> @testing-library/user-event @testing-library/jest-dom # 附加组件
<pkgmgr> add -D @playwright/test && <pkgmgr> exec playwright install # E2E (Playwright)
<pkgmgr> add -D cypress # E2E (Cypress)
pip install pytest pytest-mock # Python
go get <testify module path> # Go
“不”:记录 INSTALL=deferred;无论如何编写完整的测试,运行命令将报告为“安装后运行”。
7. 收集轻量级指针(此处不要读取大型文件)
仅路径和廉价信号;繁重的读取发生在写入时(由你执行,或者如果卸载任务则由 scout 执行)。此处不要完整读取规范、design.md 或源文件。
使用文件工具:
- 列出
docs/specs/下最近修改的 3 个规范路径(仅路径)。 - 确定指导性规范:这些文件实现的特性目录
docs/specs/NNNN-<feature>/(或单个docs/specs/NNNN-<feature>.md),通过分支/特性名称或接触的表面进行匹配(如果存在docs/scope/条目,则指向它)。记录其路径以及是否在其旁边(docs/specs/NNNN-<feature>/verify.md)存在verify.md。此契约是测试追溯的依据;它可能不在最近的 3 个路径中。当存在指导性规范时,设置TRACE_TO_CONTRACT = yes,否则为no。 - 注意项目根目录下是否存在
design.md;仅当范围内有 组件 或 页面/流程 文件时使用其路径,否则为none。 - 读取
AGENTS.md(规范文件;如果不存在则读取CLAUDE.md)作为项目上下文(简短且廉价)。还要注意构建方法一行:团队选择的切片塑造方法,记录在范围标头(或根目录AGENTS.md)中,例如薄端到端路径、可用的最薄核心循环、占位符上的 UI 优先 shell、每阶段完整的用户旅程。它不会分支逻辑;而是校准你在编写时的判断(步骤 8,规则 a)。 - 读取
package.json,注意scripts.test。当存在test脚本时,RUN_COMMAND=<pkgmgr> test(对于 npm 为<pkgmgr> run test);仅当没有时才使用原始调用(例如<pkgmgr> exec vitest run)。
7.5 询问是否运行测试套件(始终询问)
Ask: "将为 <N> 个更改的文件编写测试。编写后运行测试套件吗?" (header: "运行测试?")
- "是的,运行并修复至通过": "执行测试套件;我将修复任何测试错误并标记测试捕获的真正错误"
- "跳过,仅编写": "编写测试并提供手动运行和验证说明"
设置 RUN_AFTER = yes | no 并在写入时应用。
8. 编写测试套件(主线程)
主线程自行编写测试。不要生成编写器。将此技能的文件夹解析为绝对路径,现在(仅在现在,写入时)读取 agent-prompt.md 和 writing-guide.md:agent-prompt.md 是你的操作模板,writing-guide.md 是你遵循的策略、工具规则、迭代循环和报告格式。读取正在测试的更改文件是唯一的昂贵部分;对于大型或不熟悉的集合,仅将读取任务卸载给一个只读的 scout 子代理(使用最便宜的模型,Claude Code:haiku,不继承会话模型),它返回一个紧凑的映射,然后从中进行编写。
要应用的输入(你收集的标记值):
- 单元测试工具、E2E 工具、附加工具、
INSTALL状态;testDir、filePattern、包管理器、堆栈/框架、packageRoot;分类后的范围(每个文件路径及其类别:逻辑 / 组件 / 页面流程 / API 服务器 / CLI);RUN_COMMAND、RUN_AFTER;项目上下文以及构建方法一行;最近的 3 个规范路径或none(仅当与你测试的内容相关时读取);design.md路径或none;TRACE_TO_CONTRACT、指导性规范路径和verify.md路径(每个如果不存在则为none)。 - 要应用的两条规则:(a) 让构建方法校准哪些行为对于此切片是持久真实的(将这些锁定为稳定的断言),而不是该切片有意设计为虚假的临时脚手架(不要断言计划尚未构建的真实实现,例如,在模拟数据的 shell 上对真实后端的期望)。(b) 当
TRACE_TO_CONTRACT = yes时,读取验收标准(如果存在verify.md,则优先使用其已解析的AC-N标记的检查列表,否则使用规范的## Requirements部分)并锁定持久的标准:为每个可以固定为稳定断言的标准创建一个自动化测试,每个测试使用其覆盖的AC-N进行标记(例如covers: AC-3注释,或测试标题中的AC-3),以便测试套件追溯到契约。切勿伪造无法自动化的标准(视觉/手动/环境,例如“电子邮件实际到达”);将其记录在NOT_COVERED中,格式为AC-N, <为什么不可自动化> → 委托给 /check verify 手动步骤。
单体仓库(来自步骤 1b 的多个包根目录):依次编写每个根目录的测试套件,范围限定在其根目录的文件、工具和包管理器(如果较大,则将其根目录的文件读取任务卸载给各自的 scout)。单个根目录(常见情况)