关于 tdd
面向 helpmetest 的测试驱动开发与测试维护 Skill,指导编码 Agent 先规划场景、编写测试并验证,再分析故障。它还要求在测试操作前后向用户说明进展。
主要功能
- 01先写测试的功能与修复流程
- 02任务和功能制品规划
- 03场景覆盖优先级排序
- 04交互式测试探索与验证
- 05失败分类与修复指引
- 06测试操作前后说明进展
适用场景
- 01以测试优先启动功能或重构
- 02为现有功能补充测试
- 03诊断并修复失败测试
MCP 相关
该 Skill 指导 Agent 使用 helpmetest MCP 工具检查状态、搜索制品、交互运行和验证测试;MCP 不可用时可改用 `helpmetest `。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ help-me-test/ skills --skill tdd安装后在 Claude Code 中输入/tdd确认已加载。
附带文件references/examples/wishlist-example.md
详细介绍
你是谁: 如果
.helpmetest/SOUL.md存在,请阅读它——它定义了你的角色。
没有 MCP? 使用
helpmetest <command>,而不是 MCP 工具。
🔴 先写测试。
修改代码 → 运行测试。 新功能 → 先写测试,再写代码。 测试就是规范。测试通过,工作才算完成。 没有测试 = 尚未完成。
讲述你的操作
创建测试、生成制品或运行测试时,绝不能保持沉默。 始终告诉用户:
- 之前: 你准备做什么以及为什么(覆盖什么场景、防范什么风险)
- 之后: 发生了什么——结果、制品包含什么、测试为何失败
- 接下来: 你将做什么,以及即将遇到的决策点
沉默意味着用户完全不知道你做了什么,也不知道原因。
测试——编写、生成、修复
先了解情况(始终如此)
在做任何事情之前,检查现有内容:
helpmetest_status()
helpmetest_search_artifacts({ query: "" })
helpmetest_search_artifacts({ type: "Tasks" })
- 测试已经失败?→ 这是优先事项,而不是创建新测试
- 任务制品正在进行中?→ 恢复处理,不要从头开始
- 功能制品已存在?→ 使用它们,不要重新探索
使用场景
“我需要构建一些东西”(TDD)
新功能、Bug 修复或重构。先写测试——它们定义了“完成”的含义。
1. 创建任务制品以跟踪工作:
{
"id": "tasks-[feature-name]",
"type": "Tasks",
"content": {
"overview": "What this implements and why",
"tasks": [
{ "id": "1.0", "title": "Write all tests first", "status": "pending", "priority": "critical" },
{ "id": "2.0", "title": "Implement to make tests pass", "status": "pending" },
{ "id": "3.0", "title": "All green — review for gaps", "status": "pending" }
]
}
}
2. 创建功能制品,在编写任何测试之前列出所有场景:
{
"id": "feature-[name]",
"type": "Feature",
"content": {
"goal": "What this feature does",
"functional": [
{ "name": "User can do X", "given": "...", "when": "...", "then": "...", "tags": ["priority:critical"], "test_ids": [] }
],
"edge_cases": [],
"bugs": []
}
}
3. 在编写任何测试之前规划覆盖范围。 对于每项功能,按以下四种类型列出场景:
| 类型 | 始终? | 要问的问题 |
|---|---|---|
| 关键路径 | ✅ 始终 | 一切正常时,真实用户会怎么做? |
| 错误路径 | ✅ 始终 | 在生产环境中,最可能出现的 3 种故障方式是什么? |
| 边界条件 | 涉及数据/逻辑时 | 行为会在哪个阈值处发生变化? |
| 边缘情况 | 有选择地 | QA 工程师会测试哪些开发人员不会测试的情况? |
为每个场景标记:立即编写(高/关键优先级)/ 发布前编写(中优先级)/ 跳过(仅外观问题、已有覆盖或结果不可靠)。不要测试所有内容——测试出问题会造成损失的内容。
4. 编写所有测试——正常路径、边缘情况、错误路径——然后再开始实现。失败的测试就是你的规范。
5. 逐步实现——选择优先级最高的失败测试,让它通过,再继续下一个。
6. 完成条件: 所有测试均通过,并且你已检查是否遗漏边缘情况。
“为现有功能编写测试”
功能已经存在(或刚由其他人实现)。你的工作仅限于编写测试。
1. 阅读功能制品 — helpmetest_get_artifact({ id: "feature-X" })。如果不存在,请先根据你掌握的信息创建一个。
2. 编写测试前先进行交互式探索 — 使用 helpmetest_run_interactive_command 逐步运行场景。观察真实行为后编写的测试会使用真实选择器,并反映实际时序。根据描述编写测试只是在猜测。
As <persona>
Go To <url>
# Execute each Given/When/Then step, observe what actually happens
3. 编写每个测试前,先大声回答:
“如果这个测试通过了,但功能实际上有问题,那么在客户报告之前,我们会漏掉用户的什么投诉?”
将答案写为 [Documentation] 中的 PROTECTS: 行。这是测试对用户作出的承诺——不是可有可无的套话。如果无法用一句话回答,说明场景还需要进一步思考,而不是编写测试。
**4. 按 priority:critical、high、medium 的顺序优先编写测试。对于每个测试:
- 至少包含 5 个有意义的步骤
- 验证业务结果(数据已保存、状态已更改),而不只是元素可见
- 对任何注册/电子邮件字段,使用
Create Fake Email——绝不硬编码 [Documentation]必须以PROTECTS: <此测试捕获的用户投诉>开头
5. 验证每个测试:在将其关联到场景之前,先使用 fix-tests 进行验证。如果功能损坏时测试仍能通过,就必须重写——只有验证器返回 PASS,测试才算完成。
6. 关联测试 — 只有在验证通过后,才将每个测试 ID 添加到功能制品中的 scenario.test_ids。
7. 运行并修复 — 如果新编写的测试失败,请参阅下方的“修复损坏的测试”。
“修复损坏的测试”/“测试失败”
首先:了解失败模式
检查近期代码更改:
git diff --stat HEAD
git log --oneline -5
将已更改的文件映射到可能的原因:
components/、pages/→ 选择器更改auth/、session/→ 身份验证状态问题api/、routes/→ 后端错误或响应结构发生变化
然后获取测试历史记录:helpmetest_status({ id: "test-id", testRunLimit: 10 })
分类:
- 代码更改后持续失败 → 选择器/行为发生变化
- PASS/FAIL 交替出现且错误不断变化 → 隔离问题(共享状态、测试顺序依赖)
- 超时/元素不可见 → 时序问题
- 身份验证/会话错误 → 状态未正确恢复
- 测试输出中出现后端错误 → 真实缺陷,而非测试问题
交互式复现——修复前务必执行
逐个运行失败的步骤:
As <persona>
Go To <url>
# Execute each step, observe what actually happens at the point of failure
对于“找不到元素”:列出该类型的所有元素,并尝试其他选择器。 对于“值错误”:检查实际显示的内容与测试预期的内容。 对于超时:尝试延长等待时间,并检查元素是否会出现。
判断:是测试问题还是应用程序缺陷?
- 测试问题(选择器发生变化、时序问题、预期错误)→ 修复测试,并在保存前通过交互式方式验证修复
- 应用程序缺陷(功能确实损坏)→ 在
feature.bugs[]中记录,并将Feature.status更新为 "broken" 或 "partial"
UI 更改后许多测试都失败了?
按顺序逐个处理。对于每个测试:
- 对失败进行分类(通常是选择器或时序问题)
- 交互式复现
- 修复
- 运行以确认
不要靠猜测进行大范围修复——一次错误的修复会导致两个测试损坏。
重构后测试已过时?
- 获取测试列表:
helpmetest_status() - 对于每个失败的测试,检查功能制品中的场景是否仍符合预期行为
- 如果代码是事实依据 → 更新测试
- 如果测试没有问题,而重构破坏了行为 → 记录回归问题
编写测试
结构
As <persona> # auth state — always first
Go To <url>
# Given — establish precondition
<steps>
# When — perform the action
<steps>
# Then — verify the outcome
<assertions>
# Persistence check (if relevant)
Reload
<re-assert that state survived>
文档格式
每个测试都必须在 [Documentation] 中包含四行明确说明:
[Documentation]
... Given: <precondition — what state the system is in before the action>
... When: <action — what the user/system does>
... Then: <outcome — what is asserted, specifically>
... Risk: <what silent failure this catches — user complaint if this test were deleted>
完整示例:
[Documentation]
... Given: registered user with valid account
... When: submits login form with wrong password
... Then: sees "Invalid email or password" error, remains on login page, is NOT authenticated
... Risk: silent login failure — attacker gets in, or user is confused with no feedback
Given/When/Then——优质描述的特点:
- ✅
Given: registered user with valid account— 明确的前置条件 - ✅
When: submits login form with wrong password— 确切的操作 - ✅
Then: sees "Invalid email or password" error, remains on login page, is NOT authenticated— 具体列出多项断言 - ❌
Given: user is logged in | When: they do something | Then: it works— 内容模糊,测试失败时毫无参考价值
语言规则——描述必须让产品经理也能读懂:
- ✅ 使用用户操作和可见结果来描述
- ❌ 绝不写 CSS 选择器、XPath 或 DOM 类名(
.keyword-line.current、#submit-btn、div[data-id]) - ❌ 绝不写 JavaScript 内部细节、变量名或调试 API(
replayDebug.currentKeyword、window.__state) - ❌ 绝不写 Robot Framework 语法、关键字名称或技术实现细节
- 选择器应写在测试主体中。描述应面向产品经理。
错误:
Given: replay loaded with .banner-keywords visible
When: user clicks .banner-next and replayDebug.currentKeyword changes
Then: .keyword-line.current text matches window.replayDebug.currentKeyword.split(/ {2,}/)[0]
正确:
Given: a test replay is open and paused
When: user steps forward then backward through keywords using the navigation buttons
Then: the highlighted keyword in the banner matches the current playback position after each navigation
Risk——优质示例:
- ✅
Risk: users completing checkout get charged without receiving an order confirmation - ✅
Risk: users typing wrong passwords are silently logged in or shown a blank screen - ✅
Risk: profile email changes silently fail — user sees stale email with no indication - ❌
Risk: the login form breaks— 太模糊了,具体哪里出问题?谁会注意到? - ❌
Risk: the form doesn't submit— 这描述的是测试要做什么,而不是测试要防范什么
行内注释
每个不明显的步骤上方都必须添加一条面向产品经理、而非工程师的 # comment。
注释应说明某个步骤为何存在、用户正在经历什么,或测试在检查什么——而不是说明关键字的作用。
# User opens an existing test replay — this is the entry point for debugging failed tests
Go To https://helpmetest.example.com/test/some-test
# The banner shows the current keyword being replayed — it must stay in sync with what's actually executing
Click css=.banner-next
# After stepping forward, the highlighted keyword in the banner must change to match the new position
# If this fails, users see the wrong keyword highlighted while debugging — they investigate the wrong step
${label}= Get Text css=.keyword-line.current .keyword-text
Should Contain ${label} ${expected_keyword}
以下情况必须添加注释:
- 任何
Javascript调用——说明它读取或更改了什么用户可见的状态 - 任何
Hover或Sleep——说明界面为何需要这一步(悬停以显示隐藏元素,等待动画完成) - 任何多步骤断言组——说明这组断言共同验证了什么
- 任何不属于明显页面导航的设置步骤
优质测试的特点
✅ 验证业务结果——数据已保存、筛选器已应用、订单已创建 ✅ 如果功能损坏,测试就会失败 ✅ 包含 5 个或更多有意义的步骤 ✅ 检查状态是否发生变化,而不只是检查按钮是否存在
❌ 只导航到页面并统计元素数量 ❌ 点击某个元素,却不检查发生了什么 ❌ 功能损坏时测试仍然通过
测试命名
格式:User can <action> 或 <Feature> <behavior>
- ✅
User can update profile email - ✅
Cart total updates when quantity changes - ❌
MyApp Login Test - ❌
SiteName Checkout
身份验证
使用一次 Save As <StateName> 来保存身份验证状态。在每个测试中使用 As <StateName> 复用该状态——绝不要在测试中重新进行身份验证。
电子邮件
使用 Create Fake Email——绝不要硬编码 [email protected]。硬编码的电子邮件地址会在第二次运行时失效。
${email}= Create Fake Email
Fill Text input[name=email] ${email}
${code}= Get Email Verification Code ${email}
本地主机
如果要测试本地服务,请先设置代理:
helpmetest_proxy({ action: "start", domain: "dev.local", sourcePort: 3000 })
编写任何测试之前,先确认代理可正常工作。详情请参阅 proxy 技能。
标签
priority:critical|high|medium|lowfeature:[feature-name]type:e2e|smoke|regression
完成标准
- ✅ 所有测试均已通过
- ✅ 所有
priority:critical场景都有test_ids - ✅ 每个测试的
[Documentation]中都有Given:、When:、Then:、Risk:行 - ✅ 每个测试在关联之前都已通过
/fix-tests - ✅ Bug 已记录在
feature.bugs[]中 - ✅ 已更新
Feature.status(working/broken/partial) - ✅ Tasks 工件全部完成
最终摘要格式
结尾绝不要写“创建了 N 个测试,其中 M 个通过”。请使用以下格式:
## What you can now trust works
- <user-facing statement> (test: <id>)
- <user-facing statement> (test: <id>)
## What's still unprotected
- <what could silently break with no test catching it>
## Bugs found
- <bug description> — documented in feature.bugs[]
如果第一部分无法用面向用户的语言来写,你的测试就还没有完成。