[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6GK3GWFKV85FSTT1YE1S0X0":3,"skill-same:01M6GK3GWFKV85FSTT1YE1S0X0":66},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":27,"publishedAt":31,"linkOnly":32,"markdownContent":33,"markdownBody":34,"frontMatter":35,"refName":40,"commitSha":41,"skillDirectory":42,"forksCount":13,"repositoryHtmlUrl":43,"repositoryHomepageUrl":6,"ownerHtmlUrl":44,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":45,"install":46,"resources":59},"01M6GK3GWFKV85FSTT1YE1S0X0","测试驱动开发",null,"实现任何功能或 bug 修复时，在编写实现代码前使用 (触发词：TDD)","lihah111222333-cloud\u002FSuper-Dolphin-Agent-Cockpit","lihah111222333-cloud","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F254297992?v=4","01M6GJZ7ZK31PVRBS5XQWS56ET","01M6GJZ84Q0ANFYMPVK56PYSNW",0,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Flihah111222333-cloud\u002FSuper-Dolphin-Agent-Cockpit\u002Fblob\u002Fmain\u002F.agent\u002Fskills\u002F测试驱动开发\u002FSKILL.md",[17,22],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",{"categoryNo":23,"categoryName":24,"taxonomyType":25,"taxonomyTypeDesc":26},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[28,5,29,30],"testing","tdd","bug","2026-11-10 10:12:30",false,"---\nname: \"测试驱动开发\"\ndescription: \"实现任何功能或 bug 修复时，在编写实现代码前使用 (触发词：TDD)\"\ntrigger_words: [\"@测试驱动开发\", \"@tdd\", \"@TDD\"]\n---\n\n# 测试驱动开发（TDD）\n\n## 概览\n\n先写测试。看它失败。再写最少的代码让它通过。\n\n**核心原则：** 如果你没有看见测试失败，就不知道它是否测试了正确的东西。\n\n**违反规则的字面要求，就是违反规则精神。**\n\n## 何时使用\n\n**始终使用：**\n- 新功能\n- Bug 修复\n- 重构\n- 行为变更\n\n**例外（询问你的协作者）：**\n- 一次性原型\n- 生成代码\n- 配置文件\n\n如果你在想“这次就跳过 TDD”，停下。这是合理化借口。\n\n## 铁律\n\n```\nNO PRODUCTION CODE WITHOUT A FAILING TEST FIRST\n```\n\n在测试前写了代码？删除它。重新开始。\n\n**没有例外：**\n- 不要把它保留为“参考”\n- 不要在写测试时“改造”它\n- 不要看它\n- 删除就是删除\n\n从测试重新实现。就是这样。\n\n## 红-绿-重构\n\n```dot\ndigraph tdd_cycle {\n    rankdir=LR;\n    red [label=\"RED\\nWrite failing test\", shape=box, style=filled, fillcolor=\"#ffcccc\"];\n    verify_red [label=\"Verify fails\\ncorrectly\", shape=diamond];\n    green [label=\"GREEN\\nMinimal code\", shape=box, style=filled, fillcolor=\"#ccffcc\"];\n    verify_green [label=\"Verify passes\\nAll green\", shape=diamond];\n    refactor [label=\"REFACTOR\\nClean up\", shape=box, style=filled, fillcolor=\"#ccccff\"];\n    next [label=\"Next\", shape=ellipse];\n\n    red -> verify_red;\n    verify_red -> green [label=\"yes\"];\n    verify_red -> red [label=\"wrong\\nfailure\"];\n    green -> verify_green;\n    verify_green -> refactor [label=\"yes\"];\n    verify_green -> green [label=\"no\"];\n    refactor -> verify_green [label=\"stay\\ngreen\"];\n    verify_green -> next;\n    next -> red;\n}\n```\n\n### RED：编写失败测试\n\n写一个最小测试，展示应该发生什么。\n\n\u003CGood>\n```typescript\ntest('retries failed operations 3 times', async () => {\n  let attempts = 0;\n  const operation = () => {\n    attempts++;\n    if (attempts \u003C 3) throw new Error('fail');\n    return 'success';\n  };\n\n  const result = await retryOperation(operation);\n\n  expect(result).toBe('success');\n  expect(attempts).toBe(3);\n});\n```\n名称清楚，测试真实行为，只测一件事\n\u003C\u002FGood>\n\n\u003CBad>\n```typescript\ntest('retry works', async () => {\n  const mock = jest.fn()\n    .mockRejectedValueOnce(new Error())\n    .mockRejectedValueOnce(new Error())\n    .mockResolvedValueOnce('success');\n  await retryOperation(mock);\n  expect(mock).toHaveBeenCalledTimes(3);\n});\n```\n名称含糊，测试的是 mock 而不是代码\n\u003C\u002FBad>\n\n**要求：**\n- 一个行为\n- 名称清晰\n- 真实代码（除非不可避免，否则不用 mock）\n\n### 验证 RED：看它失败\n\n**强制执行。绝不要跳过。**\n\n```bash\n# Go 单文件改动\n.\u002Fscripts\u002Ftest_with_guard.sh path\u002Fto\u002Ffile.go\n\n# Go 包改动\n.\u002Fscripts\u002Ftest_with_guard.sh .\u002Faffected\u002Fpackage -count=1\n\n# frontend-app 改动\ncd frontend-app && npm test -- path\u002Fto\u002Ftest.test.ts\n```\n\n确认：\n- 测试失败（不是报错）\n- 失败消息符合预期\n- 因功能缺失而失败（不是拼写错误）\n\n**测试通过？** 你在测试已有行为。修正测试。\n\n**测试报错？** 修复错误，重新运行，直到它正确失败。\n\n### GREEN：最小代码\n\n写最简单的代码让测试通过。\n\n\u003CGood>\n```typescript\nasync function retryOperation\u003CT>(fn: () => Promise\u003CT>): Promise\u003CT> {\n  for (let i = 0; i \u003C 3; i++) {\n    try {\n      return await fn();\n    } catch (e) {\n      if (i === 2) throw e;\n    }\n  }\n  throw new Error('unreachable');\n}\n```\n刚好足够通过\n\u003C\u002FGood>\n\n\u003CBad>\n```typescript\nasync function retryOperation\u003CT>(\n  fn: () => Promise\u003CT>,\n  options?: {\n    maxRetries?: number;\n    backoff?: 'linear' | 'exponential';\n    onRetry?: (attempt: number) => void;\n  }\n): Promise\u003CT> {\n  \u002F\u002F YAGNI\n}\n```\n过度设计\n\u003C\u002FBad>\n\n不要添加功能、重构其他代码，或做超出测试要求的“改进”。\n\n### 验证 GREEN：看它通过\n\n**强制执行。**\n\n```bash\nnpm test path\u002Fto\u002Ftest.test.ts\n```\n\n确认：\n- 测试通过\n- 其他测试仍通过\n- 输出干净（没有错误、警告）\n\n**测试失败？** 修代码，不修测试。\n\n**其他测试失败？** 现在就修。\n\n### REFACTOR：清理\n\n只有在 green 之后：\n- 移除重复\n- 改进命名\n- 提取 helper\n\n保持测试为绿。不要添加行为。\n\n### 重复\n\n为下一个功能写下一个失败测试。\n\n## 好测试\n\n| 质量 | 好 | 坏 |\n|---------|------|-----|\n| **最小** | 一件事。名称里有 “and”？拆开。 | `test('validates email and domain and whitespace')` |\n| **清晰** | 名称描述行为 | `test('test1')` |\n| **展示意图** | 展示期望 API | 掩盖代码应该做什么 |\n\n## 为什么顺序重要\n\n**“我之后再写测试来验证它能工作”**\n\n代码之后写的测试会立即通过。立即通过什么也证明不了：\n- 可能测试了错误内容\n- 可能测试实现而不是行为\n- 可能漏掉你忘记的边界情况\n- 你从未看见它抓住 bug\n\n测试先行迫使你看到测试失败，证明它确实测试了某些东西。\n\n**“我已经手动测试了所有边界情况”**\n\n手动测试是临时的。你以为自己测了所有内容，但：\n- 没有测试记录\n- 代码变更时无法重跑\n- 压力下很容易忘记情况\n- “我试的时候能用” ≠ 全面\n\n自动化测试是系统化的。它每次都以同样方式运行。\n\n**“删除 X 小时工作是浪费”**\n\n沉没成本谬误。时间已经花掉了。现在的选择是：\n- 删除并用 TDD 重写（再花 X 小时，高置信度）\n- 保留它并事后加测试（30 分钟，低置信度，很可能有 bug）\n\n真正的“浪费”是保留你不能信任的代码。没有真实测试的可工作代码就是技术债。\n\n**“TDD 太教条，务实意味着要适配”**\n\nTDD 就是务实：\n- 提交前发现 bug（比事后调试更快）\n- 防止回归（测试立即捕获破坏）\n- 记录行为（测试展示如何使用代码）\n- 支持重构（放心修改，测试会捕获破坏）\n\n“务实”捷径 = 在生产中调试 = 更慢。\n\n**“事后测试也能达到同样目标，这重在精神而不是仪式”**\n\n不。事后测试回答“它做了什么？”测试先行回答“它应该做什么？”\n\n事后测试会被你的实现影响。你测试的是你构建了什么，而不是需求是什么。你验证的是记得的边界情况，不是先发现的边界情况。\n\n测试先行迫使你在实现前发现边界情况。事后测试只是验证你是否记住了一切（你不会）。\n\n30 分钟事后测试 ≠ TDD。你获得覆盖率，但失去测试有效性的证明。\n\n## 常见合理化借口\n\n| 借口 | 现实 |\n|--------|---------|\n| “太简单，不用测” | 简单代码也会坏。测试只要 30 秒。 |\n| “我之后再测” | 立即通过的测试什么也证明不了。 |\n| “事后测试也能达到同样目标” | 事后测试 = “这做了什么？” 测试先行 = “这应该做什么？” |\n| “已经手动测过” | 临时测试 ≠ 系统化。没有记录，无法重跑。 |\n| “删除 X 小时太浪费” | 沉没成本谬误。保留未验证代码是技术债。 |\n| “保留作参考，先写测试” | 你会改造它。这就是事后测试。删除就是删除。 |\n| “需要先探索” | 可以。丢弃探索结果，然后从 TDD 开始。 |\n| “测试很难 = 设计不清” | 听测试的。难测试 = 难使用。 |\n| “TDD 会拖慢我” | TDD 比调试快。务实 = 测试先行。 |\n| “手动测试更快” | 手动无法证明边界情况。每次变更都要重测。 |\n| “现有代码没有测试” | 你正在改进它。为现有代码补测试。 |\n\n## 红旗：停止并重新开始\n\n- 先写代码后写测试\n- 实现后补测试\n- 测试立即通过\n- 说不清测试为什么失败\n- “稍后”添加测试\n- 为“这次例外”找理由\n- “我已经手动测过”\n- “事后测试也能达到同样目的”\n- “重在精神，不是仪式”\n- “保留作参考” 或 “改造现有代码”\n- “已经花了 X 小时，删除太浪费”\n- “TDD 太教条，我很务实”\n- “这次不一样，因为……”\n\n**这些都意味着：删除代码。从 TDD 重新开始。**\n\n## 示例：Bug 修复\n\n**Bug：** 空 email 被接受\n\n**RED**\n```typescript\ntest('rejects empty email', async () => {\n  const result = await submitForm({ email: '' });\n  expect(result.error).toBe('Email required');\n});\n```\n\n**验证 RED**\n```bash\n$ npm test\nFAIL: expected 'Email required', got undefined\n```\n\n**GREEN**\n```typescript\nfunction submitForm(data: FormData) {\n  if (!data.email?.trim()) {\n    return { error: 'Email required' };\n  }\n  \u002F\u002F ...\n}\n```\n\n**验证 GREEN**\n```bash\n$ npm test\nPASS\n```\n\n**REFACTOR**\n必要时提取多字段验证。\n\n## 验证检查清单\n\n标记工作完成前：\n\n- [ ] 每个新函数\u002F方法都有测试\n- [ ] 实现前看过每个测试失败\n- [ ] 每个测试都因预期原因失败（功能缺失，而不是拼写错误）\n- [ ] 写了最小代码让每个测试通过\n- [ ] 所有测试通过\n- [ ] 输出干净（没有错误、警告）\n- [ ] 测试使用真实代码（仅在不可避免时使用 mock）\n- [ ] 覆盖边界情况和错误\n\n不能勾选所有项？你跳过了 TDD。重新开始。\n\n## 卡住时\n\n| 问题 | 解决方案 |\n|---------|----------|\n| 不知道如何测试 | 写理想 API。先写断言。询问你的协作者。 |\n| 测试太复杂 | 设计太复杂。简化接口。 |\n| 必须 mock 一切 | 代码耦合过高。使用依赖注入。 |\n| 测试设置巨大 | 提取 helper。仍复杂？简化设计。 |\n\n## 调试集成\n\n发现 bug？写一个失败测试复现它。遵循 TDD 循环。测试证明修复并防止回归。\n\n绝不要在没有测试的情况下修 bug。\n\n## 测试反模式\n\n添加 mock 或测试工具时，读取 @testing-anti-patterns.md，避免常见陷阱：\n- 测试 mock 行为而不是真实行为\n- 向生产类添加仅供测试使用的方法\n- 在不理解依赖的情况下 mock\n\n## 最终规则\n\n```\nProduction code → test exists and failed first\nOtherwise → not TDD\n```\n\n除非你的协作者允许，否则没有例外。\n","# 测试驱动开发（TDD）\n\n## 概览\n\n先写测试。看它失败。再写最少的代码让它通过。\n\n**核心原则：** 如果你没有看见测试失败，就不知道它是否测试了正确的东西。\n\n**违反规则的字面要求，就是违反规则精神。**\n\n## 何时使用\n\n**始终使用：**\n- 新功能\n- Bug 修复\n- 重构\n- 行为变更\n\n**例外（询问你的协作者）：**\n- 一次性原型\n- 生成代码\n- 配置文件\n\n如果你在想“这次就跳过 TDD”，停下。这是合理化借口。\n\n## 铁律\n\n```\nNO PRODUCTION CODE WITHOUT A FAILING TEST FIRST\n```\n\n在测试前写了代码？删除它。重新开始。\n\n**没有例外：**\n- 不要把它保留为“参考”\n- 不要在写测试时“改造”它\n- 不要看它\n- 删除就是删除\n\n从测试重新实现。就是这样。\n\n## 红-绿-重构\n\n```dot\ndigraph tdd_cycle {\n    rankdir=LR;\n    red [label=\"RED\\nWrite failing test\", shape=box, style=filled, fillcolor=\"#ffcccc\"];\n    verify_red [label=\"Verify fails\\ncorrectly\", shape=diamond];\n    green [label=\"GREEN\\nMinimal code\", shape=box, style=filled, fillcolor=\"#ccffcc\"];\n    verify_green [label=\"Verify passes\\nAll green\", shape=diamond];\n    refactor [label=\"REFACTOR\\nClean up\", shape=box, style=filled, fillcolor=\"#ccccff\"];\n    next [label=\"Next\", shape=ellipse];\n\n    red -> verify_red;\n    verify_red -> green [label=\"yes\"];\n    verify_red -> red [label=\"wrong\\nfailure\"];\n    green -> verify_green;\n    verify_green -> refactor [label=\"yes\"];\n    verify_green -> green [label=\"no\"];\n    refactor -> verify_green [label=\"stay\\ngreen\"];\n    verify_green -> next;\n    next -> red;\n}\n```\n\n### RED：编写失败测试\n\n写一个最小测试，展示应该发生什么。\n\n\u003CGood>\n```typescript\ntest('retries failed operations 3 times', async () => {\n  let attempts = 0;\n  const operation = () => {\n    attempts++;\n    if (attempts \u003C 3) throw new Error('fail');\n    return 'success';\n  };\n\n  const result = await retryOperation(operation);\n\n  expect(result).toBe('success');\n  expect(attempts).toBe(3);\n});\n```\n名称清楚，测试真实行为，只测一件事\n\u003C\u002FGood>\n\n\u003CBad>\n```typescript\ntest('retry works', async () => {\n  const mock = jest.fn()\n    .mockRejectedValueOnce(new Error())\n    .mockRejectedValueOnce(new Error())\n    .mockResolvedValueOnce('success');\n  await retryOperation(mock);\n  expect(mock).toHaveBeenCalledTimes(3);\n});\n```\n名称含糊，测试的是 mock 而不是代码\n\u003C\u002FBad>\n\n**要求：**\n- 一个行为\n- 名称清晰\n- 真实代码（除非不可避免，否则不用 mock）\n\n### 验证 RED：看它失败\n\n**强制执行。绝不要跳过。**\n\n```bash\n# Go 单文件改动\n.\u002Fscripts\u002Ftest_with_guard.sh path\u002Fto\u002Ffile.go\n\n# Go 包改动\n.\u002Fscripts\u002Ftest_with_guard.sh .\u002Faffected\u002Fpackage -count=1\n\n# frontend-app 改动\ncd frontend-app && npm test -- path\u002Fto\u002Ftest.test.ts\n```\n\n确认：\n- 测试失败（不是报错）\n- 失败消息符合预期\n- 因功能缺失而失败（不是拼写错误）\n\n**测试通过？** 你在测试已有行为。修正测试。\n\n**测试报错？** 修复错误，重新运行，直到它正确失败。\n\n### GREEN：最小代码\n\n写最简单的代码让测试通过。\n\n\u003CGood>\n```typescript\nasync function retryOperation\u003CT>(fn: () => Promise\u003CT>): Promise\u003CT> {\n  for (let i = 0; i \u003C 3; i++) {\n    try {\n      return await fn();\n    } catch (e) {\n      if (i === 2) throw e;\n    }\n  }\n  throw new Error('unreachable');\n}\n```\n刚好足够通过\n\u003C\u002FGood>\n\n\u003CBad>\n```typescript\nasync function retryOperation\u003CT>(\n  fn: () => Promise\u003CT>,\n  options?: {\n    maxRetries?: number;\n    backoff?: 'linear' | 'exponential';\n    onRetry?: (attempt: number) => void;\n  }\n): Promise\u003CT> {\n  \u002F\u002F YAGNI\n}\n```\n过度设计\n\u003C\u002FBad>\n\n不要添加功能、重构其他代码，或做超出测试要求的“改进”。\n\n### 验证 GREEN：看它通过\n\n**强制执行。**\n\n```bash\nnpm test path\u002Fto\u002Ftest.test.ts\n```\n\n确认：\n- 测试通过\n- 其他测试仍通过\n- 输出干净（没有错误、警告）\n\n**测试失败？** 修代码，不修测试。\n\n**其他测试失败？** 现在就修。\n\n### REFACTOR：清理\n\n只有在 green 之后：\n- 移除重复\n- 改进命名\n- 提取 helper\n\n保持测试为绿。不要添加行为。\n\n### 重复\n\n为下一个功能写下一个失败测试。\n\n## 好测试\n\n| 质量 | 好 | 坏 |\n|---------|------|-----|\n| **最小** | 一件事。名称里有 “and”？拆开。 | `test('validates email and domain and whitespace')` |\n| **清晰** | 名称描述行为 | `test('test1')` |\n| **展示意图** | 展示期望 API | 掩盖代码应该做什么 |\n\n## 为什么顺序重要\n\n**“我之后再写测试来验证它能工作”**\n\n代码之后写的测试会立即通过。立即通过什么也证明不了：\n- 可能测试了错误内容\n- 可能测试实现而不是行为\n- 可能漏掉你忘记的边界情况\n- 你从未看见它抓住 bug\n\n测试先行迫使你看到测试失败，证明它确实测试了某些东西。\n\n**“我已经手动测试了所有边界情况”**\n\n手动测试是临时的。你以为自己测了所有内容，但：\n- 没有测试记录\n- 代码变更时无法重跑\n- 压力下很容易忘记情况\n- “我试的时候能用” ≠ 全面\n\n自动化测试是系统化的。它每次都以同样方式运行。\n\n**“删除 X 小时工作是浪费”**\n\n沉没成本谬误。时间已经花掉了。现在的选择是：\n- 删除并用 TDD 重写（再花 X 小时，高置信度）\n- 保留它并事后加测试（30 分钟，低置信度，很可能有 bug）\n\n真正的“浪费”是保留你不能信任的代码。没有真实测试的可工作代码就是技术债。\n\n**“TDD 太教条，务实意味着要适配”**\n\nTDD 就是务实：\n- 提交前发现 bug（比事后调试更快）\n- 防止回归（测试立即捕获破坏）\n- 记录行为（测试展示如何使用代码）\n- 支持重构（放心修改，测试会捕获破坏）\n\n“务实”捷径 = 在生产中调试 = 更慢。\n\n**“事后测试也能达到同样目标，这重在精神而不是仪式”**\n\n不。事后测试回答“它做了什么？”测试先行回答“它应该做什么？”\n\n事后测试会被你的实现影响。你测试的是你构建了什么，而不是需求是什么。你验证的是记得的边界情况，不是先发现的边界情况。\n\n测试先行迫使你在实现前发现边界情况。事后测试只是验证你是否记住了一切（你不会）。\n\n30 分钟事后测试 ≠ TDD。你获得覆盖率，但失去测试有效性的证明。\n\n## 常见合理化借口\n\n| 借口 | 现实 |\n|--------|---------|\n| “太简单，不用测” | 简单代码也会坏。测试只要 30 秒。 |\n| “我之后再测” | 立即通过的测试什么也证明不了。 |\n| “事后测试也能达到同样目标” | 事后测试 = “这做了什么？” 测试先行 = “这应该做什么？” |\n| “已经手动测过” | 临时测试 ≠ 系统化。没有记录，无法重跑。 |\n| “删除 X 小时太浪费” | 沉没成本谬误。保留未验证代码是技术债。 |\n| “保留作参考，先写测试” | 你会改造它。这就是事后测试。删除就是删除。 |\n| “需要先探索” | 可以。丢弃探索结果，然后从 TDD 开始。 |\n| “测试很难 = 设计不清” | 听测试的。难测试 = 难使用。 |\n| “TDD 会拖慢我” | TDD 比调试快。务实 = 测试先行。 |\n| “手动测试更快” | 手动无法证明边界情况。每次变更都要重测。 |\n| “现有代码没有测试” | 你正在改进它。为现有代码补测试。 |\n\n## 红旗：停止并重新开始\n\n- 先写代码后写测试\n- 实现后补测试\n- 测试立即通过\n- 说不清测试为什么失败\n- “稍后”添加测试\n- 为“这次例外”找理由\n- “我已经手动测过”\n- “事后测试也能达到同样目的”\n- “重在精神，不是仪式”\n- “保留作参考” 或 “改造现有代码”\n- “已经花了 X 小时，删除太浪费”\n- “TDD 太教条，我很务实”\n- “这次不一样，因为……”\n\n**这些都意味着：删除代码。从 TDD 重新开始。**\n\n## 示例：Bug 修复\n\n**Bug：** 空 email 被接受\n\n**RED**\n```typescript\ntest('rejects empty email', async () => {\n  const result = await submitForm({ email: '' });\n  expect(result.error).toBe('Email required');\n});\n```\n\n**验证 RED**\n```bash\n$ npm test\nFAIL: expected 'Email required', got undefined\n```\n\n**GREEN**\n```typescript\nfunction submitForm(data: FormData) {\n  if (!data.email?.trim()) {\n    return { error: 'Email required' };\n  }\n  \u002F\u002F ...\n}\n```\n\n**验证 GREEN**\n```bash\n$ npm test\nPASS\n```\n\n**REFACTOR**\n必要时提取多字段验证。\n\n## 验证检查清单\n\n标记工作完成前：\n\n- [ ] 每个新函数\u002F方法都有测试\n- [ ] 实现前看过每个测试失败\n- [ ] 每个测试都因预期原因失败（功能缺失，而不是拼写错误）\n- [ ] 写了最小代码让每个测试通过\n- [ ] 所有测试通过\n- [ ] 输出干净（没有错误、警告）\n- [ ] 测试使用真实代码（仅在不可避免时使用 mock）\n- [ ] 覆盖边界情况和错误\n\n不能勾选所有项？你跳过了 TDD。重新开始。\n\n## 卡住时\n\n| 问题 | 解决方案 |\n|---------|----------|\n| 不知道如何测试 | 写理想 API。先写断言。询问你的协作者。 |\n| 测试太复杂 | 设计太复杂。简化接口。 |\n| 必须 mock 一切 | 代码耦合过高。使用依赖注入。 |\n| 测试设置巨大 | 提取 helper。仍复杂？简化设计。 |\n\n## 调试集成\n\n发现 bug？写一个失败测试复现它。遵循 TDD 循环。测试证明修复并防止回归。\n\n绝不要在没有测试的情况下修 bug。\n\n## 测试反模式\n\n添加 mock 或测试工具时，读取 @testing-anti-patterns.md，避免常见陷阱：\n- 测试 mock 行为而不是真实行为\n- 向生产类添加仅供测试使用的方法\n- 在不理解依赖的情况下 mock\n\n## 最终规则\n\n```\nProduction code → test exists and failed first\nOtherwise → not TDD\n```\n\n除非你的协作者允许，否则没有例外。\n",{"name":5,"description":7,"trigger_words":36},[37,38,39],"@测试驱动开发","@tdd","@TDD","main","287849f4ce06aaaa7e8f517126f73b016043d332",".agent\u002Fskills\u002F测试驱动开发","https:\u002F\u002Fgithub.com\u002Flihah111222333-cloud\u002FSuper-Dolphin-Agent-Cockpit","https:\u002F\u002Fgithub.com\u002Flihah111222333-cloud","2026-10-11 02:13:17",{"source":8,"skillName":5,"commands":47,"prompt":57,"zipAvailable":58},[48,51,54],{"tool":49,"command":50},"npx","npx skills add lihah111222333-cloud\u002FSuper-Dolphin-Agent-Cockpit --skill \"测试驱动开发\"",{"tool":52,"command":53},"bunx","bunx skills add lihah111222333-cloud\u002FSuper-Dolphin-Agent-Cockpit --skill \"测试驱动开发\"",{"tool":55,"command":56},"pnpm","pnpm dlx skills add lihah111222333-cloud\u002FSuper-Dolphin-Agent-Cockpit --skill \"测试驱动开发\"","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Flihah111222333-cloud\u002FSuper-Dolphin-Agent-Cockpit\u002Fblob\u002Fmain\u002F.agent\u002Fskills\u002F测试驱动开发\u002FSKILL.md",true,[60],{"path":61,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":64,"sourceHtmlUrl":65},"testing-anti-patterns.md","REFERENCE","参考文档",8022,"https:\u002F\u002Fgithub.com\u002Flihah111222333-cloud\u002FSuper-Dolphin-Agent-Cockpit\u002Fblob\u002Fmain\u002F.agent\u002Fskills\u002F测试驱动开发\u002Ftesting-anti-patterns.md",[67,95,111,134,150,164],{"skillNo":68,"name":69,"nameZh":6,"description":70,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":71,"categories":72,"keywords":76,"publishedAt":31},"01M6GK59PDCBRZQ6D03BGXM8XM","mcp-lsp-project-config","配置、安装、检查、修复或迁移项目级 mcp-lsp stdio MCP，覆盖 Codex、Claude Code 和 Google Antigravity，以及 Windows 原生、WSL 原生和 Windows 宿主通过 wsl.exe 桥接 Linux sidecar 的自动选型。用户提到项目 MCP、LSP MCP、.codex\u002Fconfig.toml、.mcp.json、.agents\u002Fmcp_config.json、Codex 中 LSP 工具有时直接暴露有时只在延迟工具目录、ALL_TOOLS 过滤为空、工具已显示但调用报 client is closing、EOF、Transport closed，或希望三个客户端共用仓库内 mcp-lsp 时使用。","https:\u002F\u002Fgithub.com\u002Flihah111222333-cloud\u002FSuper-Dolphin-Agent-Cockpit\u002Fblob\u002Fmain\u002Fbin\u002FLSP\u002Fmcp-lsp-project-config-skill\u002FSKILL.md",[73],{"categoryNo":74,"categoryName":75,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发",[77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94],"mcp lsp project config","MCP","config","lsp","project","mcp lsp","google","linux","antigravity","codex","windows","code","Tools","json","toml","client","工具","closing",{"skillNo":96,"name":97,"nameZh":6,"description":98,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":101,"publishedAt":31},"01M6GK57H2A7XK3QZ00HCV9XM0","注释规范","当在 super-agent-v3 中编写或审查 Go 函数级中文注释、React hooks\u002Fservice\u002Fcontroller 注释，或处理注释守卫失败时使用。","https:\u002F\u002Fgithub.com\u002Flihah111222333-cloud\u002FSuper-Dolphin-Agent-Cockpit\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002F注释规范\u002FSKILL.md",[],[97,102,103,104,105,106,107,108,109,110],"go","react","controller","中文","hooks","service","v3","react hooks","comments",{"skillNo":112,"name":113,"nameZh":6,"description":114,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":115,"categories":116,"keywords":120,"publishedAt":31},"01M6GK545MTTE5X1Q4NF92PNRN","字段守卫","当新增、删除、重命名或跨层传递结构化字段，以及修改 DTO、mapper、schema、API\u002FRPC、事件、配置、store、序列化或前后端契约时自动使用；追踪生产源到全部消费端并建立 fail-fast 字段守卫。","https:\u002F\u002Fgithub.com\u002Flihah111222333-cloud\u002FSuper-Dolphin-Agent-Cockpit\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002F字段守卫\u002FSKILL.md",[117],{"categoryNo":118,"categoryName":119,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000010","后端与接口",[113,121,122,123,124,125,79,126,127,128,129,130,131,132,133],"后端","event","payload","store","mapping","rpc","fast","api","guard","消费","schema","drift","contract",{"skillNo":135,"name":121,"nameZh":6,"description":136,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":137,"categories":138,"keywords":141,"publishedAt":31},"01M6GK50VVC89B3MMSTKBG76ZS","在 super-agent-v3 中编写、审查或重构 Go 后端代码时使用。","https:\u002F\u002Fgithub.com\u002Flihah111222333-cloud\u002FSuper-Dolphin-Agent-Cockpit\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002F后端\u002FSKILL.md",[139,140],{"categoryNo":118,"categoryName":119,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":74,"categoryName":75,"taxonomyType":20,"taxonomyTypeDesc":21},[121,142,143,144,145,102,78,146,147,148,108,149,124],"sqlc","backend","cmd","golang","runner","platform","module","fx",{"skillNo":151,"name":152,"nameZh":6,"description":153,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":154,"categories":155,"keywords":159,"publishedAt":31},"01M6GK4YP5DTSBD02AF8YSRNME","前端","当在 super-agent-v3 中开发、审查或修复当前 React\u002FVite 前端 UI 时使用；前端源码只在 frontend-app。","https:\u002F\u002Fgithub.com\u002Flihah111222333-cloud\u002FSuper-Dolphin-Agent-Cockpit\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002F前端\u002FSKILL.md",[156],{"categoryNo":157,"categoryName":158,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000009","前端开发",[160,161,162,108,163,103],"frontend","vite","app","ui",{"skillNo":165,"name":166,"nameZh":6,"description":167,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":168,"categories":169,"keywords":170,"publishedAt":31},"01M6GK4WWB9A6N51VPCZHC3NB1","全量项目地图生成","当在 super-agent-v3 中生成、更新或校验 docs\u002Fdoc\u002Fcodemap 项目地图、AI index 或能力清单时使用。","https:\u002F\u002Fgithub.com\u002Flihah111222333-cloud\u002FSuper-Dolphin-Agent-Cockpit\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002F全量项目地图生成\u002FSKILL.md",[],[166,171,172,108,173,174,175,176,177],"地图","生成","codemap","校验","doc","index","docs"]