[{"data":1,"prerenderedAt":2933},["ShallowReactive",2],{"navigation":3,"\u002Fcore-features\u002Ftest-debug":189,"\u002Fcore-features\u002Ftest-debug-surround":2928},[4,35,57,75,101,123,149,171],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":34},"第 1 章：认识 Claude Code","i-lucide-rocket","\u002Fintro","1.intro",[10,14,18,22,26,30],{"title":11,"path":12,"stem":13},"什么是 Claude Code","\u002Fintro\u002Fwhat-is-claude-code","1.intro\u002F1.what-is-claude-code",{"title":15,"path":16,"stem":17},"Claude Code 与 Copilot、Cursor、Windsurf 的本质区别","\u002Fintro\u002Fvs-competitors","1.intro\u002F2.vs-competitors",{"title":19,"path":20,"stem":21},"AI 编程助手生态全景与选型指南","\u002Fintro\u002Fecosystem-guide","1.intro\u002F3.ecosystem-guide",{"title":23,"path":24,"stem":25},"LLM 的概率本质","\u002Fintro\u002Fllm-probability","1.intro\u002F4.llm-probability",{"title":27,"path":28,"stem":29},"从聊天机器人到 Agent","\u002Fintro\u002Ffrom-chatbot-to-agent","1.intro\u002F5.from-chatbot-to-agent",{"title":31,"path":32,"stem":33},"Claude Code 的 Agentic Loop 全拆解","\u002Fintro\u002Fagentic-loop","1.intro\u002F6.agentic-loop",false,{"title":36,"icon":37,"path":38,"stem":39,"children":40,"page":34},"第 2 章：安装与配置","i-lucide-settings","\u002Fsetup","2.setup",[41,45,49,53],{"title":42,"path":43,"stem":44},"系统要求与安装方式","\u002Fsetup\u002Fsystem-requirements","2.setup\u002F1.system-requirements",{"title":46,"path":47,"stem":48},"认证、登录与多账户管理","\u002Fsetup\u002Fauthentication","2.setup\u002F2.authentication",{"title":50,"path":51,"stem":52},"选择你的界面","\u002Fsetup\u002Fchoose-interface","2.setup\u002F3.choose-interface",{"title":54,"path":55,"stem":56},"Coding Plan","\u002Fsetup\u002Fcoding-plan","2.setup\u002F4.coding-plan",{"title":58,"icon":59,"path":60,"stem":61,"children":62,"page":34},"第 3 章：快速上手","i-lucide-hand","\u002Fquickstart","3.quickstart",[63,67,71],{"title":64,"path":65,"stem":66},"启动、交互模式与基本命令","\u002Fquickstart\u002Fstartup","3.quickstart\u002F1.startup",{"title":68,"path":69,"stem":70},"让 Claude 理解你的项目","\u002Fquickstart\u002Fcodebase-understanding","3.quickstart\u002F2.codebase-understanding",{"title":72,"path":73,"stem":74},"第一次代码变更","\u002Fquickstart\u002Ffirst-change","3.quickstart\u002F3.first-change",{"title":76,"icon":77,"path":78,"stem":79,"children":80,"page":34},"第 4 章：核心功能","i-lucide-laptop","\u002Fcore-features","4.core-features",[81,85,89,93,97],{"title":82,"path":83,"stem":84},"代码库全景扫描与模块关系分析","\u002Fcore-features\u002Fcodebase-scan","4.core-features\u002F1.codebase-scan",{"title":86,"path":87,"stem":88},"代码编辑与生成","\u002Fcore-features\u002Fedit-generate","4.core-features\u002F2.edit-generate",{"title":90,"path":91,"stem":92},"测试与调试","\u002Fcore-features\u002Ftest-debug","4.core-features\u002F3.test-debug",{"title":94,"path":95,"stem":96},"Git 工作流","\u002Fcore-features\u002Fgit-workflow","4.core-features\u002F4.git-workflow",{"title":98,"path":99,"stem":100},"工具链执行","\u002Fcore-features\u002Ftoolchain","4.core-features\u002F5.toolchain",{"title":102,"icon":103,"path":104,"stem":105,"children":106,"page":34},"第 5 章：进阶配置","i-lucide-wrench","\u002Fadvanced","5.advanced",[107,111,115,119],{"title":108,"path":109,"stem":110},"CLAUDE.md","\u002Fadvanced\u002Fclaude-md","5.advanced\u002F1.claude-md",{"title":112,"path":113,"stem":114},"Skills","\u002Fadvanced\u002Fskills","5.advanced\u002F2.skills",{"title":116,"path":117,"stem":118},"MCP","\u002Fadvanced\u002Fmcp","5.advanced\u002F3.mcp",{"title":120,"path":121,"stem":122},"Hooks 与 Plan 模式","\u002Fadvanced\u002Fhooks-plan","5.advanced\u002F4.hooks-plan",{"title":124,"icon":125,"path":126,"stem":127,"children":128,"page":34},"第 6 章：实战开发","i-lucide-hammer","\u002Fpractice","6.practice",[129,133,137,141,145],{"title":130,"path":131,"stem":132},"需求分析与架构设计","\u002Fpractice\u002Frequirements-architecture","6.practice\u002F1.requirements-architecture",{"title":134,"path":135,"stem":136},"项目脚手架搭建与技术选型","\u002Fpractice\u002Fscaffolding","6.practice\u002F2.scaffolding",{"title":138,"path":139,"stem":140},"核心功能实现","\u002Fpractice\u002Fcore-features","6.practice\u002F3.core-features",{"title":142,"path":143,"stem":144},"测试覆盖、代码审查与质量调优","\u002Fpractice\u002Ftesting-quality","6.practice\u002F4.testing-quality",{"title":146,"path":147,"stem":148},"部署上线与成果分享","\u002Fpractice\u002Fdeployment","6.practice\u002F5.deployment",{"title":150,"icon":151,"path":152,"stem":153,"children":154,"page":34},"第 7 章：心法层","i-lucide-brain","\u002Fmindset","7.mindset",[155,159,163,167],{"title":156,"path":157,"stem":158},"提示词设计原则","\u002Fmindset\u002Fprompt-design","7.mindset\u002F1.prompt-design",{"title":160,"path":161,"stem":162},"上下文管理策略","\u002Fmindset\u002Fcontext-management","7.mindset\u002F2.context-management",{"title":164,"path":165,"stem":166},"安全与权限控制","\u002Fmindset\u002Fsecurity","7.mindset\u002F3.security",{"title":168,"path":169,"stem":170},"Boris Cherny 的 9 条实战心法与团队推广经验","\u002Fmindset\u002Fboris-cherny-tips","7.mindset\u002F4.boris-cherny-tips",{"title":172,"icon":173,"path":174,"stem":175,"children":176,"page":34},"附录","i-lucide-paperclip","\u002Fappendix","8.appendix",[177,181,185],{"title":178,"path":179,"stem":180},"常用命令速查表","\u002Fappendix\u002Fa.command-cheatsheet","8.appendix\u002Fa.command-cheatsheet",{"title":182,"path":183,"stem":184},"AI 核心术语汇编","\u002Fappendix\u002Fb.ai-terminology","8.appendix\u002Fb.ai-terminology",{"title":186,"path":187,"stem":188},"资源链接与延伸阅读","\u002Fappendix\u002Fc.resources","8.appendix\u002Fc.resources",{"id":190,"title":90,"body":191,"description":197,"extension":2922,"links":2923,"meta":2924,"navigation":1492,"path":91,"seo":2926,"stem":92,"__hash__":2927},"docs\u002F4.core-features\u002F3.test-debug.md",{"type":192,"value":193,"toc":2874},"minimark",[194,198,203,206,211,214,359,376,380,383,390,400,405,411,415,418,454,457,461,464,468,471,476,482,485,490,496,503,508,514,521,527,533,536,540,543,549,555,608,612,615,619,622,628,632,638,642,649,654,774,777,782,853,856,860,1253,1256,1274,1278,1281,1285,1290,1296,1301,1304,1321,1326,1332,1337,1340,1675,1680,1686,1691,1694,1698,1703,1709,1714,1720,1725,1731,1735,1738,1742,1747,1753,1758,1764,1769,1775,1779,1782,1817,1820,1824,1830,1834,1837,1871,1875,1878,1882,1888,1894,1908,1914,1928,1934,1948,1952,1957,1963,1968,1974,1979,1985,1989,1992,2192,2196,2200,2203,2209,2212,2248,2252,2255,2261,2265,2268,2274,2278,2289,2295,2299,2305,2547,2551,2554,2558,2561,2587,2591,2594,2640,2644,2647,2651,2655,2701,2705,2731,2735,2738,2775,2778,2870],[195,196,197],"p",{},"测试与调试是软件开发的两大支柱，也是 Claude Code 展现其 Agentic 能力最充分的场景之一。不同于传统 IDE 中孤立的代码补全或静态分析，Claude Code 能够在一个连续的代理循环中完成\"编写测试 → 运行测试 → 解析失败 → 定位 Bug → 修复代码 → 验证通过\"的完整闭环。本章将深入探讨如何利用 Claude Code 进行高效的测试驱动开发、自动化测试生成、迭代式 Bug 修复以及复杂问题的系统性调试。",[199,200,202],"h2",{"id":201},"_1-运行测试套件","1. 运行测试套件",[195,204,205],{},"Claude Code 通过 Bash 工具执行测试命令，并通过读取输出结果来理解测试状态。它能够识别主流测试框架的输出格式，并据此判断测试通过或失败。",[207,208,210],"h3",{"id":209},"_11-支持的测试框架","1.1 支持的测试框架",[195,212,213],{},"Claude Code 通过 Bash 工具调用测试命令，然后将 stdout\u002Fstderr 输出喂给 LLM 解析。由于 LLM 具备通用文本理解能力，它能够解析大多数测试框架的输出格式，但这不是框架级别的原生集成。以下列出常见测试框架及其典型命令：",[215,216,217,233],"table",{},[218,219,220],"thead",{},[221,222,223,227,230],"tr",{},[224,225,226],"th",{},"框架",[224,228,229],{},"语言\u002F生态",[224,231,232],{},"典型命令",[234,235,236,255,267,280,293,309,322,335,347],"tbody",{},[221,237,238,242,245],{},[239,240,241],"td",{},"Jest",[239,243,244],{},"JavaScript\u002FNode.js",[239,246,247,251,252],{},[248,249,250],"code",{},"npm test"," \u002F ",[248,253,254],{},"npx jest",[221,256,257,260,262],{},[239,258,259],{},"Vitest",[239,261,244],{},[239,263,264],{},[248,265,266],{},"npx vitest",[221,268,269,272,275],{},[239,270,271],{},"pytest",[239,273,274],{},"Python",[239,276,277],{},[248,278,279],{},"pytest -v",[221,281,282,285,288],{},[239,283,284],{},"Go Test",[239,286,287],{},"Go",[239,289,290],{},[248,291,292],{},"go test .\u002F...",[221,294,295,298,301],{},[239,296,297],{},"JUnit",[239,299,300],{},"Java",[239,302,303,251,306],{},[248,304,305],{},"mvn test",[248,307,308],{},".\u002Fgradlew test",[221,310,311,314,317],{},[239,312,313],{},"RSpec",[239,315,316],{},"Ruby",[239,318,319],{},[248,320,321],{},"bundle exec rspec",[221,323,324,327,330],{},[239,325,326],{},"Playwright",[239,328,329],{},"E2E (JS\u002FTS)",[239,331,332],{},[248,333,334],{},"npx playwright test",[221,336,337,340,342],{},[239,338,339],{},"Cypress",[239,341,329],{},[239,343,344],{},[248,345,346],{},"npx cypress run",[221,348,349,352,355],{},[239,350,351],{},"REST Assured",[239,353,354],{},"API (Java)",[239,356,357],{},[248,358,305],{},[195,360,361,362,365,366,365,369,365,372,375],{},"Claude Code 在生成测试时会自动检测项目中已有的测试框架和配置。它通过读取 ",[248,363,364],{},"package.json","、",[248,367,368],{},"pom.xml",[248,370,371],{},"Cargo.toml",[248,373,374],{},"go.mod"," 等配置文件，以及扫描现有的测试文件来确定应使用的框架和风格。",[207,377,379],{"id":378},"_12-要求-claude-运行测试","1.2 要求 Claude 运行测试",[195,381,382],{},"你可以用自然语言要求 Claude 运行测试，它会根据项目结构推断正确的命令。",[195,384,385,389],{},[386,387,388],"strong",{},"对话示例","：",[391,392,397],"pre",{"className":393,"code":395,"language":396},[394],"language-text","用户: 运行所有测试\n\nClaude: 我来运行项目中的所有测试。\n\nBash: npm test\n\n测试输出：\n> vitest run\n\n Test Files  12 passed (12)\n      Tests  47 passed (47)\n   Start at  10:23:45\n\n所有 47 个测试全部通过。\n","text",[248,398,395],{"__ignoreMap":399},"",[195,401,402,389],{},[386,403,404],{},"更具体的指令",[391,406,409],{"className":407,"code":408,"language":396},[394],"用户: 只运行 src\u002Fservices\u002Fpayment.ts 相关的测试，并显示覆盖率\n\nClaude: 我来运行相关测试并生成覆盖率报告。\n\nBash: npx vitest run src\u002Fservices\u002Fpayment.test.ts --coverage\n",[248,410,408],{"__ignoreMap":399},[207,412,414],{"id":413},"_13-测试工作区配置","1.3 测试工作区配置",[195,416,417],{},"为了获得最佳的测试体验，建议在开发环境中配置多标签页工作区：",[419,420,421,428,438,444],"ul",{},[422,423,424,427],"li",{},[386,425,426],{},"标签页 1",": Claude Code 终端 —— 编写提示和生成测试",[422,429,430,433,434,437],{},[386,431,432],{},"标签页 2",": 测试运行器（watch 模式）—— ",[248,435,436],{},"npm test -- --watch","，实时查看测试执行",[422,439,440,443],{},[386,441,442],{},"标签页 3",": 应用服务器 —— 保持应用运行以便进行集成验证",[422,445,446,449,450,453],{},[386,447,448],{},"标签页 4",": 覆盖率报告 —— ",[248,451,452],{},"npm run test:coverage","，追踪覆盖进度",[195,455,456],{},"这种配置让你能够在 Claude 编写测试的同时，实时看到测试出现并通过的反馈循环。",[199,458,460],{"id":459},"_2-读取错误输出","2. 读取错误输出",[195,462,463],{},"当测试失败时，Claude Code 的核心价值在于它能够理解错误输出的语义，而不仅仅是文本匹配。它会分析失败信息、堆栈跟踪和上下文，从而定位问题根因。",[207,465,467],{"id":466},"_21-解析测试失败信息","2.1 解析测试失败信息",[195,469,470],{},"Claude Code 能够理解多种类型的测试失败：",[195,472,473,389],{},[386,474,475],{},"断言失败",[391,477,480],{"className":478,"code":479,"language":396},[394],"expect(received).toBe(expected)\n\nExpected: 42\nReceived: 38\n",[248,481,479],{"__ignoreMap":399},[195,483,484],{},"Claude 会识别期望值与实际值的差异，并追溯计算逻辑。",[195,486,487,389],{},[386,488,489],{},"异常抛出",[391,491,494],{"className":492,"code":493,"language":396},[394],"TypeError: Cannot read property 'name' of undefined\n    at UserService.getUserProfile (src\u002Fservices\u002Fuser.ts:45:12)\n",[248,495,493],{"__ignoreMap":399},[195,497,498,499,502],{},"Claude 会沿着堆栈跟踪定位到具体代码行，分析变量为何为 ",[248,500,501],{},"undefined","。",[195,504,505,389],{},[386,506,507],{},"异步超时",[391,509,512],{"className":510,"code":511,"language":396},[394],"Timeout - Async callback was not invoked within the 5000 ms timeout\n",[248,513,511],{"__ignoreMap":399},[195,515,516,517,520],{},"Claude 会检查异步操作的实现，识别是否缺少 ",[248,518,519],{},"await","、Promise 未正确返回，或存在死锁。",[195,522,523,526],{},[386,524,525],{},"快照不匹配","（Jest\u002FVitest）：",[391,528,531],{"className":529,"code":530,"language":396},[394],"Snapshot name: `Button renders correctly 1`\nReceived value does not match stored snapshot\n",[248,532,530],{"__ignoreMap":399},[195,534,535],{},"Claude 会分析 UI 变更是有意的设计修改还是意外的回归，并决定更新快照或修复组件。",[207,537,539],{"id":538},"_22-交互式调试循环","2.2 交互式调试循环",[195,541,542],{},"最有效的调试方式是将 Claude 作为实时调试伙伴，通过持续对话缩小问题范围：",[391,544,547],{"className":545,"code":546,"language":396},[394],"用户: 测试失败了，帮我看看\n\nClaude: 我来看看测试失败的原因。\n\n[读取测试输出]\n\n失败的是 UserService.create 测试，错误是 \"unique constraint violation\"。\n让我检查一下测试代码和数据库约束。\n\n[读取测试文件和模型定义]\n\n我发现问题：测试之间共享了数据库状态，上一个测试创建的用户\n没有清理，导致唯一键冲突。我来修复测试的 setUp\u002FtearDown。\n\nEdit: tests\u002Fservices\u002Fuser.test.ts\n...\n",[248,548,546],{"__ignoreMap":399},[195,550,551,554],{},[386,552,553],{},"高级技巧","：将终端输出通过管道直接喂给 Claude：",[391,556,560],{"className":557,"code":558,"language":559,"meta":399,"style":399},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","npm run test 2>&1 | tee test-output.txt | claude -p \"分析这些测试失败\"\n","bash",[248,561,562],{"__ignoreMap":399},[563,564,567,571,575,578,582,585,588,591,593,596,599,602,605],"span",{"class":565,"line":566},"line",1,[563,568,570],{"class":569},"sBMFI","npm",[563,572,574],{"class":573},"sfazB"," run",[563,576,577],{"class":573}," test",[563,579,581],{"class":580},"sMK4o"," 2>&1",[563,583,584],{"class":580}," |",[563,586,587],{"class":569}," tee",[563,589,590],{"class":573}," test-output.txt",[563,592,584],{"class":580},[563,594,595],{"class":569}," claude",[563,597,598],{"class":573}," -p",[563,600,601],{"class":580}," \"",[563,603,604],{"class":573},"分析这些测试失败",[563,606,607],{"class":580},"\"\n",[199,609,611],{"id":610},"_3-自动修复失败测试","3. 自动修复失败测试",[195,613,614],{},"Claude Code 最强大的能力之一是迭代修复循环（Iterative Fix Loop）。当测试失败时，Claude 可以自主地分析失败原因、修改代码、重新运行测试，直到所有测试通过。",[207,616,618],{"id":617},"_31-迭代修复循环","3.1 迭代修复循环",[195,620,621],{},"这个循环遵循以下模式：",[391,623,626],{"className":624,"code":625,"language":396},[394],"运行测试 → 发现失败 → 分析原因 → 修复代码 → 重新运行 → 验证通过\n   ↑                                                        |\n   └──────────────── 仍有失败 ──────────────────────────────┘\n",[248,627,625],{"__ignoreMap":399},[195,629,630,389],{},[386,631,388],{},[391,633,636],{"className":634,"code":635,"language":396},[394],"用户: 实现 calculateDiscount 函数，要求：\n  - 接受价格和折扣百分比\n  - 处理负值和超过 100% 的边界情况\n  - 结果保留两位小数\n  - 先写测试，再写实现\n\nClaude: 好的，我按照 TDD 流程来实现。\n\n[步骤 1] 编写失败的测试\nWrite: src\u002Futils\u002Fdiscount.test.ts\n\n[步骤 2] 运行测试（确认失败）\nBash: npx vitest run src\u002Futils\u002Fdiscount.test.ts\n→ 测试失败（函数不存在）\n\n[步骤 3] 编写最小实现\nWrite: src\u002Futils\u002Fdiscount.ts\n\n[步骤 4] 重新运行测试\nBash: npx vitest run src\u002Futils\u002Fdiscount.test.ts\n→ 测试通过\n\n[步骤 5] 重构\nEdit: src\u002Futils\u002Fdiscount.ts（优化代码可读性）\n\n[步骤 6] 最终验证\nBash: npx vitest run src\u002Futils\u002Fdiscount.test.ts\n→ 全部通过\n\ncalculateDiscount 函数已实现，所有测试通过。\n",[248,637,635],{"__ignoreMap":399},[207,639,641],{"id":640},"_32-使用-hooks-实现自动化-tdd","3.2 使用 Hooks 实现自动化 TDD",[195,643,644,645,648],{},"通过配置 ",[248,646,647],{},".claude\u002Fsettings.json"," 中的 Hooks，可以实现真正的自动化 TDD 反馈循环：",[195,650,651,389],{},[386,652,653],{},"每次编辑后自动运行测试",[391,655,659],{"className":656,"code":657,"language":658,"meta":399,"style":399},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write|Edit\",\n        \"hook\": \"npm test -- --bail --watchAll=false 2>&1 | tail -30\"\n      }\n    ]\n  }\n}\n","json",[248,660,661,666,685,701,707,731,750,756,762,768],{"__ignoreMap":399},[563,662,663],{"class":565,"line":566},[563,664,665],{"class":580},"{\n",[563,667,669,672,676,679,682],{"class":565,"line":668},2,[563,670,671],{"class":580},"  \"",[563,673,675],{"class":674},"spNyl","hooks",[563,677,678],{"class":580},"\"",[563,680,681],{"class":580},":",[563,683,684],{"class":580}," {\n",[563,686,688,691,694,696,698],{"class":565,"line":687},3,[563,689,690],{"class":580},"    \"",[563,692,693],{"class":569},"PostToolUse",[563,695,678],{"class":580},[563,697,681],{"class":580},[563,699,700],{"class":580}," [\n",[563,702,704],{"class":565,"line":703},4,[563,705,706],{"class":580},"      {\n",[563,708,710,713,717,719,721,723,726,728],{"class":565,"line":709},5,[563,711,712],{"class":580},"        \"",[563,714,716],{"class":715},"sbssI","matcher",[563,718,678],{"class":580},[563,720,681],{"class":580},[563,722,601],{"class":580},[563,724,725],{"class":573},"Write|Edit",[563,727,678],{"class":580},[563,729,730],{"class":580},",\n",[563,732,734,736,739,741,743,745,748],{"class":565,"line":733},6,[563,735,712],{"class":580},[563,737,738],{"class":715},"hook",[563,740,678],{"class":580},[563,742,681],{"class":580},[563,744,601],{"class":580},[563,746,747],{"class":573},"npm test -- --bail --watchAll=false 2>&1 | tail -30",[563,749,607],{"class":580},[563,751,753],{"class":565,"line":752},7,[563,754,755],{"class":580},"      }\n",[563,757,759],{"class":565,"line":758},8,[563,760,761],{"class":580},"    ]\n",[563,763,765],{"class":565,"line":764},9,[563,766,767],{"class":580},"  }\n",[563,769,771],{"class":565,"line":770},10,[563,772,773],{"class":580},"}\n",[195,775,776],{},"配置后，每次 Claude 写入或编辑文件，测试套件都会自动运行。如果测试失败，Claude 会立即看到输出并修复问题。",[195,778,779,389],{},[386,780,781],{},"强制测试通过才能结束",[391,783,785],{"className":656,"code":784,"language":658,"meta":399,"style":399},"{\n  \"hooks\": {\n    \"Stop\": [\n      {\n        \"hook\": \"npm test -- --bail 2>&1 | tail -30\"\n      }\n    ]\n  }\n}\n",[248,786,787,791,803,816,820,837,841,845,849],{"__ignoreMap":399},[563,788,789],{"class":565,"line":566},[563,790,665],{"class":580},[563,792,793,795,797,799,801],{"class":565,"line":668},[563,794,671],{"class":580},[563,796,675],{"class":674},[563,798,678],{"class":580},[563,800,681],{"class":580},[563,802,684],{"class":580},[563,804,805,807,810,812,814],{"class":565,"line":687},[563,806,690],{"class":580},[563,808,809],{"class":569},"Stop",[563,811,678],{"class":580},[563,813,681],{"class":580},[563,815,700],{"class":580},[563,817,818],{"class":565,"line":703},[563,819,706],{"class":580},[563,821,822,824,826,828,830,832,835],{"class":565,"line":709},[563,823,712],{"class":580},[563,825,738],{"class":715},[563,827,678],{"class":580},[563,829,681],{"class":580},[563,831,601],{"class":580},[563,833,834],{"class":573},"npm test -- --bail 2>&1 | tail -30",[563,836,607],{"class":580},[563,838,839],{"class":565,"line":733},[563,840,755],{"class":580},[563,842,843],{"class":565,"line":752},[563,844,761],{"class":580},[563,846,847],{"class":565,"line":758},[563,848,767],{"class":580},[563,850,851],{"class":565,"line":764},[563,852,773],{"class":580},[195,854,855],{},"这个配置确保 Claude 在每次回合结束时运行测试，如果失败则继续修复，形成自我纠正的开发循环。",[207,857,859],{"id":858},"_33-完整的-tdd-hook-配置","3.3 完整的 TDD Hook 配置",[391,861,863],{"className":656,"code":862,"language":658,"meta":399,"style":399},"{\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"prompt\",\n            \"prompt\": \"检查是否在编写功能代码。如果是，确认对应的测试文件已存在。如果不存在，提醒先写测试。返回 'approve' 或提醒信息。\"\n          }\n        ]\n      }\n    ],\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write|Edit\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"npm test -- --passWithNoTests --watchAll=false\"\n            \u002F\u002F onFailure 字段的可用性请以官方文档为准\n            \u002F\u002F https:\u002F\u002Fcode.claude.com\u002Fdocs\u002Fen\u002Fhooks\n          }\n        ]\n      }\n    ],\n    \"Stop\": [\n      {\n        \"matcher\": \"*\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"npm test\"\n            \u002F\u002F onFailure 字段的可用性请以官方文档为准\n            \u002F\u002F https:\u002F\u002Fcode.claude.com\u002Fdocs\u002Fen\u002Fhooks\n          }\n        ]\n      }\n    ]\n  }\n}\n",[248,864,865,869,881,894,898,917,929,934,956,973,978,984,989,995,1008,1013,1032,1045,1050,1070,1088,1095,1101,1106,1111,1116,1121,1134,1139,1159,1172,1177,1196,1213,1218,1223,1228,1233,1238,1243,1248],{"__ignoreMap":399},[563,866,867],{"class":565,"line":566},[563,868,665],{"class":580},[563,870,871,873,875,877,879],{"class":565,"line":668},[563,872,671],{"class":580},[563,874,675],{"class":674},[563,876,678],{"class":580},[563,878,681],{"class":580},[563,880,684],{"class":580},[563,882,883,885,888,890,892],{"class":565,"line":687},[563,884,690],{"class":580},[563,886,887],{"class":569},"PreToolUse",[563,889,678],{"class":580},[563,891,681],{"class":580},[563,893,700],{"class":580},[563,895,896],{"class":565,"line":703},[563,897,706],{"class":580},[563,899,900,902,904,906,908,910,913,915],{"class":565,"line":709},[563,901,712],{"class":580},[563,903,716],{"class":715},[563,905,678],{"class":580},[563,907,681],{"class":580},[563,909,601],{"class":580},[563,911,912],{"class":573},"Write",[563,914,678],{"class":580},[563,916,730],{"class":580},[563,918,919,921,923,925,927],{"class":565,"line":733},[563,920,712],{"class":580},[563,922,675],{"class":715},[563,924,678],{"class":580},[563,926,681],{"class":580},[563,928,700],{"class":580},[563,930,931],{"class":565,"line":752},[563,932,933],{"class":580},"          {\n",[563,935,936,939,943,945,947,949,952,954],{"class":565,"line":758},[563,937,938],{"class":580},"            \"",[563,940,942],{"class":941},"swJcz","type",[563,944,678],{"class":580},[563,946,681],{"class":580},[563,948,601],{"class":580},[563,950,951],{"class":573},"prompt",[563,953,678],{"class":580},[563,955,730],{"class":580},[563,957,958,960,962,964,966,968,971],{"class":565,"line":764},[563,959,938],{"class":580},[563,961,951],{"class":941},[563,963,678],{"class":580},[563,965,681],{"class":580},[563,967,601],{"class":580},[563,969,970],{"class":573},"检查是否在编写功能代码。如果是，确认对应的测试文件已存在。如果不存在，提醒先写测试。返回 'approve' 或提醒信息。",[563,972,607],{"class":580},[563,974,975],{"class":565,"line":770},[563,976,977],{"class":580},"          }\n",[563,979,981],{"class":565,"line":980},11,[563,982,983],{"class":580},"        ]\n",[563,985,987],{"class":565,"line":986},12,[563,988,755],{"class":580},[563,990,992],{"class":565,"line":991},13,[563,993,994],{"class":580},"    ],\n",[563,996,998,1000,1002,1004,1006],{"class":565,"line":997},14,[563,999,690],{"class":580},[563,1001,693],{"class":569},[563,1003,678],{"class":580},[563,1005,681],{"class":580},[563,1007,700],{"class":580},[563,1009,1011],{"class":565,"line":1010},15,[563,1012,706],{"class":580},[563,1014,1016,1018,1020,1022,1024,1026,1028,1030],{"class":565,"line":1015},16,[563,1017,712],{"class":580},[563,1019,716],{"class":715},[563,1021,678],{"class":580},[563,1023,681],{"class":580},[563,1025,601],{"class":580},[563,1027,725],{"class":573},[563,1029,678],{"class":580},[563,1031,730],{"class":580},[563,1033,1035,1037,1039,1041,1043],{"class":565,"line":1034},17,[563,1036,712],{"class":580},[563,1038,675],{"class":715},[563,1040,678],{"class":580},[563,1042,681],{"class":580},[563,1044,700],{"class":580},[563,1046,1048],{"class":565,"line":1047},18,[563,1049,933],{"class":580},[563,1051,1053,1055,1057,1059,1061,1063,1066,1068],{"class":565,"line":1052},19,[563,1054,938],{"class":580},[563,1056,942],{"class":941},[563,1058,678],{"class":580},[563,1060,681],{"class":580},[563,1062,601],{"class":580},[563,1064,1065],{"class":573},"command",[563,1067,678],{"class":580},[563,1069,730],{"class":580},[563,1071,1073,1075,1077,1079,1081,1083,1086],{"class":565,"line":1072},20,[563,1074,938],{"class":580},[563,1076,1065],{"class":941},[563,1078,678],{"class":580},[563,1080,681],{"class":580},[563,1082,601],{"class":580},[563,1084,1085],{"class":573},"npm test -- --passWithNoTests --watchAll=false",[563,1087,607],{"class":580},[563,1089,1091],{"class":565,"line":1090},21,[563,1092,1094],{"class":1093},"sHwdD","            \u002F\u002F onFailure 字段的可用性请以官方文档为准\n",[563,1096,1098],{"class":565,"line":1097},22,[563,1099,1100],{"class":1093},"            \u002F\u002F https:\u002F\u002Fcode.claude.com\u002Fdocs\u002Fen\u002Fhooks\n",[563,1102,1104],{"class":565,"line":1103},23,[563,1105,977],{"class":580},[563,1107,1109],{"class":565,"line":1108},24,[563,1110,983],{"class":580},[563,1112,1114],{"class":565,"line":1113},25,[563,1115,755],{"class":580},[563,1117,1119],{"class":565,"line":1118},26,[563,1120,994],{"class":580},[563,1122,1124,1126,1128,1130,1132],{"class":565,"line":1123},27,[563,1125,690],{"class":580},[563,1127,809],{"class":569},[563,1129,678],{"class":580},[563,1131,681],{"class":580},[563,1133,700],{"class":580},[563,1135,1137],{"class":565,"line":1136},28,[563,1138,706],{"class":580},[563,1140,1142,1144,1146,1148,1150,1152,1155,1157],{"class":565,"line":1141},29,[563,1143,712],{"class":580},[563,1145,716],{"class":715},[563,1147,678],{"class":580},[563,1149,681],{"class":580},[563,1151,601],{"class":580},[563,1153,1154],{"class":573},"*",[563,1156,678],{"class":580},[563,1158,730],{"class":580},[563,1160,1162,1164,1166,1168,1170],{"class":565,"line":1161},30,[563,1163,712],{"class":580},[563,1165,675],{"class":715},[563,1167,678],{"class":580},[563,1169,681],{"class":580},[563,1171,700],{"class":580},[563,1173,1175],{"class":565,"line":1174},31,[563,1176,933],{"class":580},[563,1178,1180,1182,1184,1186,1188,1190,1192,1194],{"class":565,"line":1179},32,[563,1181,938],{"class":580},[563,1183,942],{"class":941},[563,1185,678],{"class":580},[563,1187,681],{"class":580},[563,1189,601],{"class":580},[563,1191,1065],{"class":573},[563,1193,678],{"class":580},[563,1195,730],{"class":580},[563,1197,1199,1201,1203,1205,1207,1209,1211],{"class":565,"line":1198},33,[563,1200,938],{"class":580},[563,1202,1065],{"class":941},[563,1204,678],{"class":580},[563,1206,681],{"class":580},[563,1208,601],{"class":580},[563,1210,250],{"class":573},[563,1212,607],{"class":580},[563,1214,1216],{"class":565,"line":1215},34,[563,1217,1094],{"class":1093},[563,1219,1221],{"class":565,"line":1220},35,[563,1222,1100],{"class":1093},[563,1224,1226],{"class":565,"line":1225},36,[563,1227,977],{"class":580},[563,1229,1231],{"class":565,"line":1230},37,[563,1232,983],{"class":580},[563,1234,1236],{"class":565,"line":1235},38,[563,1237,755],{"class":580},[563,1239,1241],{"class":565,"line":1240},39,[563,1242,761],{"class":580},[563,1244,1246],{"class":565,"line":1245},40,[563,1247,767],{"class":580},[563,1249,1251],{"class":565,"line":1250},41,[563,1252,773],{"class":580},[195,1254,1255],{},"这个配置实现了三层保障：",[1257,1258,1259,1264,1269],"ol",{},[422,1260,1261,1263],{},[386,1262,887],{},"：在编写功能代码前检查测试是否存在",[422,1265,1266,1268],{},[386,1267,693],{},"：每次编辑后自动运行测试",[422,1270,1271,1273],{},[386,1272,809],{},"：回合结束时强制测试通过，否则阻塞",[199,1275,1277],{"id":1276},"_4-调试复杂-bug","4. 调试复杂 Bug",[195,1279,1280],{},"对于复杂的 Bug，Claude Code 能够执行系统性的根因分析，而不是仅仅处理表面症状。",[207,1282,1284],{"id":1283},"_41-系统性调试流程","4.1 系统性调试流程",[195,1286,1287],{},[386,1288,1289],{},"步骤 1：描述症状",[391,1291,1294],{"className":1292,"code":1293,"language":396},[394],"用户: 用户报告结账时偶尔看到错误的订单总额。\n问题间歇性出现，难以稳定复现。订单确认页面的\n总金额与商品加税费和运费之和不符。\n",[248,1295,1293],{"__ignoreMap":399},[195,1297,1298],{},[386,1299,1300],{},"步骤 2：分析代码路径",[195,1302,1303],{},"Claude 会追踪涉及的代码路径：",[419,1305,1306,1309,1312,1315,1318],{},[422,1307,1308],{},"购物车服务（管理商品）",[422,1310,1311],{},"定价服务（计算价格、促销）",[422,1313,1314],{},"税费计算服务",[422,1316,1317],{},"运费服务",[422,1319,1320],{},"结账服务（协调各组件）",[195,1322,1323],{},[386,1324,1325],{},"步骤 3：识别根因",[391,1327,1330],{"className":1328,"code":1329,"language":396},[394],"Claude: 我分析了结账服务的代码，发现了一个关键问题：\n结账服务并行调用定价、税费和运费服务，但没有确保\n它们都操作同一版本的购物车数据。如果用户在服务调用之间\n修改了购物车（添加\u002F删除商品），每个服务可能接收到不同的购物车数据。\n\n这是典型的竞态条件（Race Condition）。\n",[248,1331,1329],{"__ignoreMap":399},[195,1333,1334],{},[386,1335,1336],{},"步骤 4：创建复现测试",[195,1338,1339],{},"Claude 会编写一个模拟并发操作的测试来稳定复现问题：",[391,1341,1345],{"className":1342,"code":1343,"language":1344,"meta":399,"style":399},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","describe('checkout race condition', () => {\n  test('should handle concurrent cart modifications during checkout', async () => {\n    const cart = await createCartWithItems([\n      { productId: 'A', price: 100 },\n      { productId: 'B', price: 200 }\n    ]);\n\n    \u002F\u002F 启动结账流程\n    const checkoutPromise = checkoutService.process(cart.id);\n\n    \u002F\u002F 在结账过程中并发修改购物车\n    await cartService.addItem(cart.id, { productId: 'C', price: 300 });\n\n    const order = await checkoutPromise;\n\n    \u002F\u002F 验证订单总额基于结账开始时的购物车快照\n    expect(order.total).toBe(300); \u002F\u002F 100 + 200，不包含 C\n  });\n});\n","typescript",[248,1346,1347,1376,1401,1422,1453,1480,1488,1494,1499,1532,1536,1541,1594,1598,1613,1617,1622,1657,1666],{"__ignoreMap":399},[563,1348,1349,1353,1357,1360,1363,1365,1368,1371,1374],{"class":565,"line":566},[563,1350,1352],{"class":1351},"s2Zo4","describe",[563,1354,1356],{"class":1355},"sTEyZ","(",[563,1358,1359],{"class":580},"'",[563,1361,1362],{"class":573},"checkout race condition",[563,1364,1359],{"class":580},[563,1366,1367],{"class":580},",",[563,1369,1370],{"class":580}," ()",[563,1372,1373],{"class":674}," =>",[563,1375,684],{"class":580},[563,1377,1378,1381,1383,1385,1388,1390,1392,1395,1397,1399],{"class":565,"line":668},[563,1379,1380],{"class":1351},"  test",[563,1382,1356],{"class":941},[563,1384,1359],{"class":580},[563,1386,1387],{"class":573},"should handle concurrent cart modifications during checkout",[563,1389,1359],{"class":580},[563,1391,1367],{"class":580},[563,1393,1394],{"class":674}," async",[563,1396,1370],{"class":580},[563,1398,1373],{"class":674},[563,1400,684],{"class":580},[563,1402,1403,1406,1409,1412,1416,1419],{"class":565,"line":687},[563,1404,1405],{"class":674},"    const",[563,1407,1408],{"class":1355}," cart",[563,1410,1411],{"class":580}," =",[563,1413,1415],{"class":1414},"s7zQu"," await",[563,1417,1418],{"class":1351}," createCartWithItems",[563,1420,1421],{"class":941},"([\n",[563,1423,1424,1427,1430,1432,1435,1438,1440,1442,1445,1447,1450],{"class":565,"line":703},[563,1425,1426],{"class":580},"      {",[563,1428,1429],{"class":941}," productId",[563,1431,681],{"class":580},[563,1433,1434],{"class":580}," '",[563,1436,1437],{"class":573},"A",[563,1439,1359],{"class":580},[563,1441,1367],{"class":580},[563,1443,1444],{"class":941}," price",[563,1446,681],{"class":580},[563,1448,1449],{"class":715}," 100",[563,1451,1452],{"class":580}," },\n",[563,1454,1455,1457,1459,1461,1463,1466,1468,1470,1472,1474,1477],{"class":565,"line":709},[563,1456,1426],{"class":580},[563,1458,1429],{"class":941},[563,1460,681],{"class":580},[563,1462,1434],{"class":580},[563,1464,1465],{"class":573},"B",[563,1467,1359],{"class":580},[563,1469,1367],{"class":580},[563,1471,1444],{"class":941},[563,1473,681],{"class":580},[563,1475,1476],{"class":715}," 200",[563,1478,1479],{"class":580}," }\n",[563,1481,1482,1485],{"class":565,"line":733},[563,1483,1484],{"class":941},"    ])",[563,1486,1487],{"class":580},";\n",[563,1489,1490],{"class":565,"line":752},[563,1491,1493],{"emptyLinePlaceholder":1492},true,"\n",[563,1495,1496],{"class":565,"line":758},[563,1497,1498],{"class":1093},"    \u002F\u002F 启动结账流程\n",[563,1500,1501,1503,1506,1508,1511,1514,1517,1519,1522,1524,1527,1530],{"class":565,"line":764},[563,1502,1405],{"class":674},[563,1504,1505],{"class":1355}," checkoutPromise",[563,1507,1411],{"class":580},[563,1509,1510],{"class":1355}," checkoutService",[563,1512,1513],{"class":580},".",[563,1515,1516],{"class":1351},"process",[563,1518,1356],{"class":941},[563,1520,1521],{"class":1355},"cart",[563,1523,1513],{"class":580},[563,1525,1526],{"class":1355},"id",[563,1528,1529],{"class":941},")",[563,1531,1487],{"class":580},[563,1533,1534],{"class":565,"line":770},[563,1535,1493],{"emptyLinePlaceholder":1492},[563,1537,1538],{"class":565,"line":980},[563,1539,1540],{"class":1093},"    \u002F\u002F 在结账过程中并发修改购物车\n",[563,1542,1543,1546,1549,1551,1554,1556,1558,1560,1562,1564,1567,1569,1571,1573,1576,1578,1580,1582,1584,1587,1590,1592],{"class":565,"line":986},[563,1544,1545],{"class":1414},"    await",[563,1547,1548],{"class":1355}," cartService",[563,1550,1513],{"class":580},[563,1552,1553],{"class":1351},"addItem",[563,1555,1356],{"class":941},[563,1557,1521],{"class":1355},[563,1559,1513],{"class":580},[563,1561,1526],{"class":1355},[563,1563,1367],{"class":580},[563,1565,1566],{"class":580}," {",[563,1568,1429],{"class":941},[563,1570,681],{"class":580},[563,1572,1434],{"class":580},[563,1574,1575],{"class":573},"C",[563,1577,1359],{"class":580},[563,1579,1367],{"class":580},[563,1581,1444],{"class":941},[563,1583,681],{"class":580},[563,1585,1586],{"class":715}," 300",[563,1588,1589],{"class":580}," }",[563,1591,1529],{"class":941},[563,1593,1487],{"class":580},[563,1595,1596],{"class":565,"line":991},[563,1597,1493],{"emptyLinePlaceholder":1492},[563,1599,1600,1602,1605,1607,1609,1611],{"class":565,"line":997},[563,1601,1405],{"class":674},[563,1603,1604],{"class":1355}," order",[563,1606,1411],{"class":580},[563,1608,1415],{"class":1414},[563,1610,1505],{"class":1355},[563,1612,1487],{"class":580},[563,1614,1615],{"class":565,"line":1010},[563,1616,1493],{"emptyLinePlaceholder":1492},[563,1618,1619],{"class":565,"line":1015},[563,1620,1621],{"class":1093},"    \u002F\u002F 验证订单总额基于结账开始时的购物车快照\n",[563,1623,1624,1627,1629,1632,1634,1637,1639,1641,1644,1646,1649,1651,1654],{"class":565,"line":1034},[563,1625,1626],{"class":1351},"    expect",[563,1628,1356],{"class":941},[563,1630,1631],{"class":1355},"order",[563,1633,1513],{"class":580},[563,1635,1636],{"class":1355},"total",[563,1638,1529],{"class":941},[563,1640,1513],{"class":580},[563,1642,1643],{"class":1351},"toBe",[563,1645,1356],{"class":941},[563,1647,1648],{"class":715},"300",[563,1650,1529],{"class":941},[563,1652,1653],{"class":580},";",[563,1655,1656],{"class":1093}," \u002F\u002F 100 + 200，不包含 C\n",[563,1658,1659,1662,1664],{"class":565,"line":1047},[563,1660,1661],{"class":580},"  }",[563,1663,1529],{"class":941},[563,1665,1487],{"class":580},[563,1667,1668,1671,1673],{"class":565,"line":1052},[563,1669,1670],{"class":580},"}",[563,1672,1529],{"class":1355},[563,1674,1487],{"class":580},[195,1676,1677],{},[386,1678,1679],{},"步骤 5：实施修复",[391,1681,1684],{"className":1682,"code":1683,"language":396},[394],"Claude: 解决方案是在结账开始时创建购物车的不可变快照，\n然后将这个快照传递给所有后续服务调用，确保一致性。\n\nEdit: src\u002Fservices\u002Fcheckout.ts\n\u002F\u002F 在结账开始时创建深度不可变快照（生产环境应使用结构化克隆或 immer 等库）\nconst cartSnapshot = structuredClone(cart);\nObject.freeze(cartSnapshot);\n\u002F\u002F 注意：Object.freeze 是浅冻结，嵌套对象仍需递归处理\n\n\u002F\u002F 所有服务使用同一个快照\nconst [pricing, tax, shipping] = await Promise.all([\n  pricingService.calculate(cartSnapshot),\n  taxService.calculate(cartSnapshot),\n  shippingService.calculate(cartSnapshot)\n]);\n",[248,1685,1683],{"__ignoreMap":399},[195,1687,1688],{},[386,1689,1690],{},"步骤 6：验证修复",[195,1692,1693],{},"Claude 会运行完整的测试套件，包括新添加的竞态条件测试，确保修复有效且没有引入回归。",[207,1695,1697],{"id":1696},"_42-调试场景示例","4.2 调试场景示例",[195,1699,1700],{},[386,1701,1702],{},"场景 A：内存泄漏",[391,1704,1707],{"className":1705,"code":1706,"language":396},[394],"用户: 应用运行一段时间后内存持续增长，最终 OOM。\n\nClaude: 我来系统性地分析这个问题。\n\n[步骤 1] 检查事件监听器是否未移除\n[步骤 2] 检查闭包是否持有大对象引用\n[步骤 3] 检查定时器是否未清理\n[步骤 4] 检查缓存是否有上限\n\n发现：UserProfile 组件在卸载时没有移除 WebSocket 监听器，\n导致每个组件实例都被持续引用。\n\nEdit: src\u002Fcomponents\u002FUserProfile.vue\n在 onUnmounted 钩子中移除监听器。\n",[248,1708,1706],{"__ignoreMap":399},[195,1710,1711],{},[386,1712,1713],{},"场景 B：性能瓶颈",[391,1715,1718],{"className":1716,"code":1717,"language":396},[394],"用户: 列表页加载超过 5 秒，数据库查询慢。\n\nClaude: 让我分析查询性能。\n\n[步骤 1] 检查 N+1 查询问题\n[步骤 2] 检查是否缺少索引\n[步骤 3] 检查是否加载了不必要的关联数据\n\n发现：ProductRepository.findAll() 对每个产品都单独查询了\n分类信息，产生 N+1 问题。建议使用 JOIN 或批量查询。\n\nEdit: src\u002Frepositories\u002Fproduct.ts\n添加 .leftJoinAndSelect('product.category', 'category')\n",[248,1719,1717],{"__ignoreMap":399},[195,1721,1722],{},[386,1723,1724],{},"场景 C：仅生产环境出现的 Bug",[391,1726,1729],{"className":1727,"code":1728,"language":396},[394],"用户: 这个 Bug 只在生产环境出现，本地无法复现。\n\nClaude: 让我分析日志和环境差异。\n\n[步骤 1] 检查环境变量差异\n[步骤 2] 检查数据库数据差异（边界数据、空值）\n[步骤 3] 检查第三方服务行为差异\n[步骤 4] 检查并发负载差异\n\n建议：在代码中添加结构化日志，记录关键变量状态。\n然后分析生产日志中的异常模式。\n",[248,1730,1728],{"__ignoreMap":399},[199,1732,1734],{"id":1733},"_5-测试生成","5. 测试生成",[195,1736,1737],{},"Claude Code 能够为现有代码生成高质量的测试，覆盖正常路径、边界情况和错误条件。",[207,1739,1741],{"id":1740},"_51-为现有代码生成测试","5.1 为现有代码生成测试",[195,1743,1744,389],{},[386,1745,1746],{},"单个文件",[391,1748,1751],{"className":1749,"code":1750,"language":396},[394],"用户: 为 src\u002Futils\u002Fvalidation.ts 编写全面的测试。\n覆盖所有导出函数、边界情况和错误条件。\n使用项目中已有的测试约定。\n",[248,1752,1750],{"__ignoreMap":399},[195,1754,1755,389],{},[386,1756,1757],{},"整个模块",[391,1759,1762],{"className":1760,"code":1761,"language":396},[394],"用户: 为 src\u002Fapi\u002F 目录下的所有模块添加测试覆盖。\n参考现有测试的风格。每创建一个测试文件就运行一次，确保通过。\n",[248,1763,1761],{"__ignoreMap":399},[195,1765,1766,389],{},[386,1767,1768],{},"基于产品规格",[391,1770,1773],{"className":1771,"code":1772,"language":396},[394],"用户: 这是购物车的产品规格：\n- 用户可以添加\u002F删除商品\n- 数量更新重新计算总额\n- 折扣码支持百分比或固定金额\n- 税费基于配送州计算\n- 满 $50 免运费\n\n针对 CartService 类编写验证所有这些行为的测试。\n",[248,1774,1772],{"__ignoreMap":399},[207,1776,1778],{"id":1777},"_52-测试生成质量","5.2 测试生成质量",[195,1780,1781],{},"Claude Code 在理想条件下生成的测试通常具有以下特点：",[419,1783,1784,1790,1799,1805,1811],{},[422,1785,1786,1789],{},[386,1787,1788],{},"遵循 Arrange-Act-Assert 模式","：结构清晰，可读性强",[422,1791,1792,389,1795,1798],{},[386,1793,1794],{},"使用工厂函数",[248,1796,1797],{},"createMockUser()"," 而非硬编码内联数据",[422,1800,1801,1804],{},[386,1802,1803],{},"覆盖边界情况","：null、空数组、边界值、并发访问",[422,1806,1807,1810],{},[386,1808,1809],{},"描述性命名","：测试名称本身就是行为文档",[422,1812,1813,1816],{},[386,1814,1815],{},"框架原生风格","：自动匹配项目使用的测试框架习惯",[195,1818,1819],{},"但实际质量取决于提示词清晰度、上下文完整性和模型版本。AI 生成质量受 prompt 质量、上下文、模型版本影响巨大，不应假设每次都能达到理想输出。",[195,1821,1822,389],{},[386,1823,388],{},[391,1825,1828],{"className":1826,"code":1827,"language":396},[394],"用户: 给这个函数写测试\n\nfunction parseDate(input: string): Date {\n  const parts = input.split('-');\n  return new Date(+parts[0], +parts[1] - 1, +parts[2]);\n}\n\nClaude: 我来为 parseDate 编写全面的测试。\n\nWrite: src\u002Futils\u002Fdate.test.ts\n\nimport { describe, test, expect } from 'vitest';\nimport { parseDate } from '.\u002Fdate';\n\ndescribe('parseDate', () => {\n  test('should parse valid ISO date string', () => {\n    const result = parseDate('2026-04-26');\n    expect(result).toEqual(new Date(2026, 3, 26));\n  });\n\n  test('should handle month boundary (January)', () => {\n    const result = parseDate('2026-01-01');\n    expect(result.getMonth()).toBe(0);\n  });\n\n  test('should handle month boundary (December)', () => {\n    const result = parseDate('2026-12-31');\n    expect(result.getMonth()).toBe(11);\n  });\n\n  test('should handle leap year date', () => {\n    const result = parseDate('2024-02-29');\n    expect(result.getDate()).toBe(29);\n  });\n\n  test('should handle empty string', () => {\n    expect(() => parseDate('')).toThrow();\n  });\n\n  test('should handle malformed input', () => {\n    expect(() => parseDate('not-a-date')).toThrow();\n  });\n\n  test('should handle single-digit month\u002Fday', () => {\n    const result = parseDate('2026-1-5');\n    expect(result).toEqual(new Date(2026, 0, 5));\n  });\n});\n",[248,1829,1827],{"__ignoreMap":399},[207,1831,1833],{"id":1832},"_53-从-0-到-80-覆盖率策略","5.3 从 0% 到 80% 覆盖率策略",[195,1835,1836],{},"对于没有测试的代码库，采用以下策略：",[1257,1838,1839,1845,1851,1857,1865],{},[422,1840,1841,1844],{},[386,1842,1843],{},"识别关键模块","：让 Claude 分析代码库，识别风险最高的模块（业务逻辑最复杂、依赖最多、Bug 历史最多）",[422,1846,1847,1850],{},[386,1848,1849],{},"优先生成关键测试","：从 ROI 最高的模块开始",[422,1852,1853,1856],{},[386,1854,1855],{},"运行覆盖率报告","：检查覆盖缺口",[422,1858,1859,389,1862],{},[386,1860,1861],{},"针对性补充",[248,1863,1864],{},"为 UserService 中未覆盖的分支编写测试",[422,1866,1867,1870],{},[386,1868,1869],{},"迭代直到达标","：每轮速度会越来越快",[199,1872,1874],{"id":1873},"_6-测试策略单元集成端到端","6. 测试策略：单元、集成、端到端",[195,1876,1877],{},"Claude Code 能够在测试金字塔的每一层发挥作用，关键在于明确各层的职责和协作方式。",[207,1879,1881],{"id":1880},"_61-测试金字塔与-claude-的协作","6.1 测试金字塔与 Claude 的协作",[391,1883,1886],{"className":1884,"code":1885,"language":396},[394],"      \u002F\\\n     \u002F  \\      E2E 测试（少量）—— 验证完整用户旅程\n    \u002F----\\\n   \u002F      \\    集成测试（中等）—— 验证组件协作\n  \u002F--------\\\n \u002F          \\  单元测试（大量）—— 验证业务逻辑\n",[248,1887,1885],{"__ignoreMap":399},[195,1889,1890,1893],{},[386,1891,1892],{},"单元测试","（最多）：",[419,1895,1896,1899,1902,1905],{},[422,1897,1898],{},"覆盖所有公共方法、边界情况和错误条件",[422,1900,1901],{},"外部依赖使用 Mock",[422,1903,1904],{},"执行最快，反馈最及时",[422,1906,1907],{},"Claude 可在几分钟内为整个模块生成",[195,1909,1910,1913],{},[386,1911,1912],{},"集成测试","（中等）：",[419,1915,1916,1919,1922,1925],{},[422,1917,1918],{},"验证多个组件协作",[422,1920,1921],{},"使用真实数据库（测试数据库），Mock 外部服务",[422,1923,1924],{},"测试数据流和交互边界",[422,1926,1927],{},"Claude 能读取多个源文件理解依赖关系",[195,1929,1930,1933],{},[386,1931,1932],{},"端到端测试","（少量）：",[419,1935,1936,1939,1942,1945],{},[422,1937,1938],{},"模拟真实用户行为",[422,1940,1941],{},"使用 Playwright、Cypress 等工具",[422,1943,1944],{},"覆盖关键用户旅程（注册 → 登录 → 核心功能）",[422,1946,1947],{},"Claude 生成 Page Object 模式，测试读起来像用户故事",[207,1949,1951],{"id":1950},"_62-各层测试示例","6.2 各层测试示例",[195,1953,1954,389],{},[386,1955,1956],{},"单元测试 —— React 组件",[391,1958,1961],{"className":1959,"code":1960,"language":396},[394],"用户: 为 SearchBar 组件写测试，使用 React Testing Library。\n测试：默认 props 渲染、用户输入触发防抖搜索、加载状态显示、\n结果正确显示、空状态消息。\n",[248,1962,1960],{"__ignoreMap":399},[195,1964,1965,389],{},[386,1966,1967],{},"集成测试 —— API 端点",[391,1969,1972],{"className":1970,"code":1971,"language":396},[394],"用户: 为 \u002Fapi\u002Forders 端点编写集成测试。\n测试所有 CRUD 操作、认证要求、验证错误和分页。\n使用 supertest。Mock 支付网关，但使用真实数据库。\n",[248,1973,1971],{"__ignoreMap":399},[195,1975,1976,389],{},[386,1977,1978],{},"E2E 测试 —— 用户注册流程",[391,1980,1983],{"className":1981,"code":1982,"language":396},[394],"用户: 用 Playwright 编写用户注册流程的 E2E 测试：\n注册 → 邮箱验证 → 首次登录 → 新手引导。\n",[248,1984,1982],{"__ignoreMap":399},[207,1986,1988],{"id":1987},"_63-测试数据工厂","6.3 测试数据工厂",[195,1990,1991],{},"Claude Code 倾向于生成可维护的测试数据工厂，而非脆弱的硬编码数据：",[391,1993,1995],{"className":1342,"code":1994,"language":1344,"meta":399,"style":399},"\u002F\u002F tests\u002Ffactories\u002Fuser.factory.ts\nlet counter = 0;\n\nexport function createUser(overrides: Partial\u003CUser> = {}): User {\n  counter++;\n  return {\n    id: `user-${counter}`,\n    email: `user${counter}@example.com`,\n    name: `Test User ${counter}`,\n    createdAt: new Date('2026-01-01'),\n    ...overrides,\n  };\n}\n",[248,1996,1997,2002,2018,2022,2063,2071,2078,2102,2128,2148,2174,2183,2188],{"__ignoreMap":399},[563,1998,1999],{"class":565,"line":566},[563,2000,2001],{"class":1093},"\u002F\u002F tests\u002Ffactories\u002Fuser.factory.ts\n",[563,2003,2004,2007,2010,2013,2016],{"class":565,"line":668},[563,2005,2006],{"class":674},"let",[563,2008,2009],{"class":1355}," counter ",[563,2011,2012],{"class":580},"=",[563,2014,2015],{"class":715}," 0",[563,2017,1487],{"class":580},[563,2019,2020],{"class":565,"line":687},[563,2021,1493],{"emptyLinePlaceholder":1492},[563,2023,2024,2027,2030,2033,2035,2039,2041,2044,2047,2050,2053,2055,2058,2061],{"class":565,"line":703},[563,2025,2026],{"class":1414},"export",[563,2028,2029],{"class":674}," function",[563,2031,2032],{"class":1351}," createUser",[563,2034,1356],{"class":580},[563,2036,2038],{"class":2037},"sHdIc","overrides",[563,2040,681],{"class":580},[563,2042,2043],{"class":569}," Partial",[563,2045,2046],{"class":580},"\u003C",[563,2048,2049],{"class":569},"User",[563,2051,2052],{"class":580},">",[563,2054,1411],{"class":580},[563,2056,2057],{"class":580}," {}):",[563,2059,2060],{"class":569}," User",[563,2062,684],{"class":580},[563,2064,2065,2068],{"class":565,"line":709},[563,2066,2067],{"class":1355},"  counter",[563,2069,2070],{"class":580},"++;\n",[563,2072,2073,2076],{"class":565,"line":733},[563,2074,2075],{"class":1414},"  return",[563,2077,684],{"class":580},[563,2079,2080,2083,2085,2088,2091,2094,2097,2100],{"class":565,"line":752},[563,2081,2082],{"class":941},"    id",[563,2084,681],{"class":580},[563,2086,2087],{"class":580}," `",[563,2089,2090],{"class":573},"user-",[563,2092,2093],{"class":580},"${",[563,2095,2096],{"class":1355},"counter",[563,2098,2099],{"class":580},"}`",[563,2101,730],{"class":580},[563,2103,2104,2107,2109,2111,2114,2116,2118,2120,2123,2126],{"class":565,"line":758},[563,2105,2106],{"class":941},"    email",[563,2108,681],{"class":580},[563,2110,2087],{"class":580},[563,2112,2113],{"class":573},"user",[563,2115,2093],{"class":580},[563,2117,2096],{"class":1355},[563,2119,1670],{"class":580},[563,2121,2122],{"class":573},"@example.com",[563,2124,2125],{"class":580},"`",[563,2127,730],{"class":580},[563,2129,2130,2133,2135,2137,2140,2142,2144,2146],{"class":565,"line":764},[563,2131,2132],{"class":941},"    name",[563,2134,681],{"class":580},[563,2136,2087],{"class":580},[563,2138,2139],{"class":573},"Test User ",[563,2141,2093],{"class":580},[563,2143,2096],{"class":1355},[563,2145,2099],{"class":580},[563,2147,730],{"class":580},[563,2149,2150,2153,2155,2158,2161,2163,2165,2168,2170,2172],{"class":565,"line":770},[563,2151,2152],{"class":941},"    createdAt",[563,2154,681],{"class":580},[563,2156,2157],{"class":580}," new",[563,2159,2160],{"class":1351}," Date",[563,2162,1356],{"class":941},[563,2164,1359],{"class":580},[563,2166,2167],{"class":573},"2026-01-01",[563,2169,1359],{"class":580},[563,2171,1529],{"class":941},[563,2173,730],{"class":580},[563,2175,2176,2179,2181],{"class":565,"line":980},[563,2177,2178],{"class":580},"    ...",[563,2180,2038],{"class":1355},[563,2182,730],{"class":580},[563,2184,2185],{"class":565,"line":986},[563,2186,2187],{"class":580},"  };\n",[563,2189,2190],{"class":565,"line":991},[563,2191,773],{"class":580},[199,2193,2195],{"id":2194},"_7-调试技巧","7. 调试技巧",[207,2197,2199],{"id":2198},"_71-日志分析","7.1 日志分析",[195,2201,2202],{},"当问题难以定位时，让 Claude 在关键路径添加结构化日志：",[391,2204,2207],{"className":2205,"code":2206,"language":396},[394],"用户: 这个请求偶尔返回 500，但错误信息不明确。\n帮我在关键路径添加日志，追踪请求处理流程。\n\nClaude: 我会在请求处理的关键节点添加结构化日志。\n\nEdit: src\u002Fmiddleware\u002FrequestLogger.ts\n添加：请求入口、认证结果、数据库查询前后、响应发送\n\nEdit: src\u002Fservices\u002ForderService.ts\n添加：订单创建参数、价格计算中间值、外部 API 调用结果\n",[248,2208,2206],{"__ignoreMap":399},[195,2210,2211],{},"然后将日志输出持续喂给 Claude 分析：",[391,2213,2215],{"className":557,"code":2214,"language":559,"meta":399,"style":399},"npm run dev 2>&1 | tee app.log | claude -p \"监控并报告任何异常模式\"\n",[248,2216,2217],{"__ignoreMap":399},[563,2218,2219,2221,2223,2226,2228,2230,2232,2235,2237,2239,2241,2243,2246],{"class":565,"line":566},[563,2220,570],{"class":569},[563,2222,574],{"class":573},[563,2224,2225],{"class":573}," dev",[563,2227,581],{"class":580},[563,2229,584],{"class":580},[563,2231,587],{"class":569},[563,2233,2234],{"class":573}," app.log",[563,2236,584],{"class":580},[563,2238,595],{"class":569},[563,2240,598],{"class":573},[563,2242,601],{"class":580},[563,2244,2245],{"class":573},"监控并报告任何异常模式",[563,2247,607],{"class":580},[207,2249,2251],{"id":2250},"_72-二分排查git-bisect","7.2 二分排查（Git Bisect）",[195,2253,2254],{},"当 Bug 是近期引入的，使用 Git 二分法快速定位：",[391,2256,2259],{"className":2257,"code":2258,"language":396},[394],"用户: 这个 Bug 上周还不存在，帮我找出引入它的提交。\n\nClaude: 我来使用 git bisect 定位问题提交。\n\nBash: git bisect start\nBash: git bisect bad HEAD        # 当前版本有 bug\nBash: git bisect good HEAD~20  # 20 个提交前正常\nBash: git bisect run npm test -- tests\u002Ffailing.test.ts\n\n[分析结果]\n\n问题提交是 a1b2c3d：\"优化查询性能\"\n该提交移除了一个必要的索引，导致特定查询超时。\n",[248,2260,2258],{"__ignoreMap":399},[207,2262,2264],{"id":2263},"_73-代码审查式调试","7.3 代码审查式调试",[195,2266,2267],{},"让 Claude 以代码审查的视角主动发现潜在问题：",[391,2269,2272],{"className":2270,"code":2271,"language":396},[394],"用户: 审查这段代码，找出可能导致 Bug 的模式。\n\nClaude: 我发现了以下潜在问题：\n\n1. [高] 第 45 行：未处理 Promise rejection，可能导致未捕获异常\n2. [中] 第 62 行：使用 == 而非 ===，类型强制可能导致意外行为\n3. [中] 第 78 行：循环中修改数组长度，可能导致索引跳过\n4. [低] 第 91 行：缺少输入验证，依赖调用方提供正确格式\n",[248,2273,2271],{"__ignoreMap":399},[207,2275,2277],{"id":2276},"_74-使用扩展思考模式extended-thinking","7.4 使用扩展思考模式（Extended Thinking）",[195,2279,2280,2281,2284,2285,2288],{},"对于复杂的架构级 Bug，可启用 Claude 的 ",[386,2282,2283],{},"Extended Thinking"," 模式（在支持该功能的模型中，Claude 会在响应前进行更深入的推理）。注意：早期社区流传的 ",[248,2286,2287],{},"ultrathink"," 关键字已被官方 Extended Thinking 机制取代。",[391,2290,2293],{"className":2291,"code":2292,"language":396},[394],"用户: 分析这个分布式系统中的数据不一致问题。\n涉及三个服务和最终一致性。\n",[248,2294,2292],{"__ignoreMap":399},[199,2296,2298],{"id":2297},"_8-claudemd-测试配置模板","8. CLAUDE.md 测试配置模板",[195,2300,2301,2302,2304],{},"在项目的 ",[248,2303,108],{}," 中添加测试规范，确保 Claude 生成的测试始终符合团队标准：",[391,2306,2310],{"className":2307,"code":2308,"language":2309,"meta":399,"style":399},"language-markdown shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","## 测试规范\n\n### 框架\n- 单元测试：Vitest\n- 组件测试：React Testing Library + Vitest\n- E2E 测试：Playwright\n- API 测试：supertest\n\n### 约定\n- 单元测试与源码同目录（Button.test.tsx 紧邻 Button.tsx）\n- E2E 测试放在 tests\u002Fe2e\u002F\n- 使用工厂函数生成测试数据（见 tests\u002Ffactories\u002F）\n- 用 msw Mock 外部 API（见 tests\u002Fmocks\u002F）\n- 每个测试文件应可独立运行\n\n### 命令\n- `npm test` —— 运行所有单元测试\n- `npm run test:e2e` —— 运行 Playwright 测试\n- `npm run test:coverage` —— 生成覆盖率报告\n- `npm run test:watch` —— watch 模式\n\n### 覆盖率阈值\n- 分支覆盖率：80%\n- 语句覆盖率：85%\n- CI 低于阈值则阻断 PR\n\n### TDD 规则\n1. 新功能必须先写失败的测试\n2. 只写刚好能通过测试的最小代码\n3. 每次绿灯后考虑重构\n4. 禁止无测试的功能代码\n","markdown",[248,2311,2312,2320,2324,2332,2340,2347,2354,2361,2365,2372,2379,2386,2393,2400,2407,2411,2418,2431,2445,2458,2472,2476,2483,2490,2497,2504,2508,2515,2523,2531,2539],{"__ignoreMap":399},[563,2313,2314,2317],{"class":565,"line":566},[563,2315,2316],{"class":580},"## ",[563,2318,2319],{"class":569},"测试规范\n",[563,2321,2322],{"class":565,"line":668},[563,2323,1493],{"emptyLinePlaceholder":1492},[563,2325,2326,2329],{"class":565,"line":687},[563,2327,2328],{"class":580},"### ",[563,2330,2331],{"class":569},"框架\n",[563,2333,2334,2337],{"class":565,"line":703},[563,2335,2336],{"class":580},"-",[563,2338,2339],{"class":1355}," 单元测试：Vitest\n",[563,2341,2342,2344],{"class":565,"line":709},[563,2343,2336],{"class":580},[563,2345,2346],{"class":1355}," 组件测试：React Testing Library + Vitest\n",[563,2348,2349,2351],{"class":565,"line":733},[563,2350,2336],{"class":580},[563,2352,2353],{"class":1355}," E2E 测试：Playwright\n",[563,2355,2356,2358],{"class":565,"line":752},[563,2357,2336],{"class":580},[563,2359,2360],{"class":1355}," API 测试：supertest\n",[563,2362,2363],{"class":565,"line":758},[563,2364,1493],{"emptyLinePlaceholder":1492},[563,2366,2367,2369],{"class":565,"line":764},[563,2368,2328],{"class":580},[563,2370,2371],{"class":569},"约定\n",[563,2373,2374,2376],{"class":565,"line":770},[563,2375,2336],{"class":580},[563,2377,2378],{"class":1355}," 单元测试与源码同目录（Button.test.tsx 紧邻 Button.tsx）\n",[563,2380,2381,2383],{"class":565,"line":980},[563,2382,2336],{"class":580},[563,2384,2385],{"class":1355}," E2E 测试放在 tests\u002Fe2e\u002F\n",[563,2387,2388,2390],{"class":565,"line":986},[563,2389,2336],{"class":580},[563,2391,2392],{"class":1355}," 使用工厂函数生成测试数据（见 tests\u002Ffactories\u002F）\n",[563,2394,2395,2397],{"class":565,"line":991},[563,2396,2336],{"class":580},[563,2398,2399],{"class":1355}," 用 msw Mock 外部 API（见 tests\u002Fmocks\u002F）\n",[563,2401,2402,2404],{"class":565,"line":997},[563,2403,2336],{"class":580},[563,2405,2406],{"class":1355}," 每个测试文件应可独立运行\n",[563,2408,2409],{"class":565,"line":1010},[563,2410,1493],{"emptyLinePlaceholder":1492},[563,2412,2413,2415],{"class":565,"line":1015},[563,2414,2328],{"class":580},[563,2416,2417],{"class":569},"命令\n",[563,2419,2420,2422,2424,2426,2428],{"class":565,"line":1034},[563,2421,2336],{"class":580},[563,2423,2087],{"class":580},[563,2425,250],{"class":573},[563,2427,2125],{"class":580},[563,2429,2430],{"class":1355}," —— 运行所有单元测试\n",[563,2432,2433,2435,2437,2440,2442],{"class":565,"line":1047},[563,2434,2336],{"class":580},[563,2436,2087],{"class":580},[563,2438,2439],{"class":573},"npm run test:e2e",[563,2441,2125],{"class":580},[563,2443,2444],{"class":1355}," —— 运行 Playwright 测试\n",[563,2446,2447,2449,2451,2453,2455],{"class":565,"line":1052},[563,2448,2336],{"class":580},[563,2450,2087],{"class":580},[563,2452,452],{"class":573},[563,2454,2125],{"class":580},[563,2456,2457],{"class":1355}," —— 生成覆盖率报告\n",[563,2459,2460,2462,2464,2467,2469],{"class":565,"line":1072},[563,2461,2336],{"class":580},[563,2463,2087],{"class":580},[563,2465,2466],{"class":573},"npm run test:watch",[563,2468,2125],{"class":580},[563,2470,2471],{"class":1355}," —— watch 模式\n",[563,2473,2474],{"class":565,"line":1090},[563,2475,1493],{"emptyLinePlaceholder":1492},[563,2477,2478,2480],{"class":565,"line":1097},[563,2479,2328],{"class":580},[563,2481,2482],{"class":569},"覆盖率阈值\n",[563,2484,2485,2487],{"class":565,"line":1103},[563,2486,2336],{"class":580},[563,2488,2489],{"class":1355}," 分支覆盖率：80%\n",[563,2491,2492,2494],{"class":565,"line":1108},[563,2493,2336],{"class":580},[563,2495,2496],{"class":1355}," 语句覆盖率：85%\n",[563,2498,2499,2501],{"class":565,"line":1113},[563,2500,2336],{"class":580},[563,2502,2503],{"class":1355}," CI 低于阈值则阻断 PR\n",[563,2505,2506],{"class":565,"line":1118},[563,2507,1493],{"emptyLinePlaceholder":1492},[563,2509,2510,2512],{"class":565,"line":1123},[563,2511,2328],{"class":580},[563,2513,2514],{"class":569},"TDD 规则\n",[563,2516,2517,2520],{"class":565,"line":1136},[563,2518,2519],{"class":580},"1.",[563,2521,2522],{"class":1355}," 新功能必须先写失败的测试\n",[563,2524,2525,2528],{"class":565,"line":1141},[563,2526,2527],{"class":580},"2.",[563,2529,2530],{"class":1355}," 只写刚好能通过测试的最小代码\n",[563,2532,2533,2536],{"class":565,"line":1161},[563,2534,2535],{"class":580},"3.",[563,2537,2538],{"class":1355}," 每次绿灯后考虑重构\n",[563,2540,2541,2544],{"class":565,"line":1174},[563,2542,2543],{"class":580},"4.",[563,2545,2546],{"class":1355}," 禁止无测试的功能代码\n",[199,2548,2550],{"id":2549},"_9-高级实践自主测试循环","9. 高级实践：自主测试循环",[195,2552,2553],{},"社区中有开发者提出类似\"自主测试循环\"的方法论，让 Claude 在真实浏览器中自动执行测试计划、发现 Bug 并修复的迭代流程。这类方法依赖第三方脚本和工作流设计，非 Claude Code 官方功能。",[207,2555,2557],{"id":2556},"_91-自主测试循环的核心思想","9.1 自主测试循环的核心思想",[195,2559,2560],{},"自主测试循环通常通过以下文件实现：",[419,2562,2563,2569,2575,2581],{},[422,2564,2565,2568],{},[248,2566,2567],{},"prepare.md"," —— 指导从测试计划生成状态文件",[422,2570,2571,2574],{},[248,2572,2573],{},"prompt.md"," —— 每次迭代执行的循环指令",[422,2576,2577,2580],{},[248,2578,2579],{},"status.json"," —— 追踪所有测试用例的状态",[422,2582,2583,2586],{},[248,2584,2585],{},"results.md"," —— 人类可读的操作日志",[207,2588,2590],{"id":2589},"_92-循环执行流程","9.2 循环执行流程",[195,2592,2593],{},"每次迭代 Claude 执行：",[1257,2595,2596,2605,2611,2617,2628,2634],{},[422,2597,2598,2601,2602,2604],{},[386,2599,2600],{},"读取状态"," —— 从 ",[248,2603,2579],{}," 了解当前测试进展",[422,2606,2607,2610],{},[386,2608,2609],{},"选择测试用例"," —— 按优先级：进行中 → 失败（尝试 \u003C 3 次）→ 未测试",[422,2612,2613,2616],{},[386,2614,2615],{},"执行测试"," —— 在浏览器中导航、点击、填写、验证",[422,2618,2619,2622,2623,2625,2626],{},[386,2620,2621],{},"记录结果"," —— 更新 ",[248,2624,2579],{}," 和 ",[248,2627,2585],{},[422,2629,2630,2633],{},[386,2631,2632],{},"处理失败"," —— 分析根因、修复代码、下次迭代重测",[422,2635,2636,2639],{},[386,2637,2638],{},"检查完成"," —— 全部通过则标记完成",[207,2641,2643],{"id":2642},"_93-注意事项","9.3 注意事项",[195,2645,2646],{},"自主测试循环需要精心设计提示词和状态管理机制。由于 Claude Code 本身不提供内置的自动化测试循环命令，这类工作流本质上是利用 Claude 的代理能力配合外部脚本实现的。建议从小范围测试开始验证可行性，再逐步扩展覆盖范围。",[199,2648,2650],{"id":2649},"_10-最佳实践与注意事项","10. 最佳实践与注意事项",[207,2652,2654],{"id":2653},"_101-有效实践","10.1 有效实践",[419,2656,2657,2670,2678,2686,2695],{},[422,2658,2659,2662,2663,2665,2666,2669],{},[386,2660,2661],{},"明确指定测试框架","：在 ",[248,2664,108],{}," 中声明 ",[248,2667,2668],{},"\"使用 Vitest 做单元测试，Playwright 做 E2E\"","，避免 Claude 猜测",[422,2671,2672,389,2675],{},[386,2673,2674],{},"指向现有测试作为风格模板",[248,2676,2677],{},"\"遵循 tests\u002Fservices\u002Fauth.test.ts 的相同模式\"",[422,2679,2680,389,2683],{},[386,2681,2682],{},"显式要求边界情况",[248,2684,2685],{},"\"包含 null 输入、空字符串、超大数值和并发访问的测试\"",[422,2687,2688,389,2691,2694],{},[386,2689,2690],{},"增量运行测试",[248,2692,2693],{},"\"为每个函数编写测试，边写边运行\"","，尽早发现问题",[422,2696,2697,2700],{},[386,2698,2699],{},"审查生成的测试","：AI 生成的测试可能有盲点 —— 确保它们测试行为而非实现细节",[207,2702,2704],{"id":2703},"_102-常见陷阱","10.2 常见陷阱",[419,2706,2707,2713,2719,2725],{},[422,2708,2709,2712],{},[386,2710,2711],{},"测试实现而非行为","：如果测试过度依赖内部实现细节，重构时测试会频繁失败",[422,2714,2715,2718],{},[386,2716,2717],{},"虚假安全感","：测试全部通过不代表产品正确工作，代码可能从未被调用（如 WooCommerce 案例中新代码存在但定价页仍调用旧 Stripe 函数）",[422,2720,2721,2724],{},[386,2722,2723],{},"死重测试","：部分 AI 生成测试可能对覆盖率无独立贡献（死重测试），需要人工审查并删除冗余",[422,2726,2727,2730],{},[386,2728,2729],{},"遗漏关键路径","：变异测试显示 AI 可能遗漏 HTTP 500 路径、空集合响应和边界条件",[207,2732,2734],{"id":2733},"_103-验证清单","10.3 验证清单",[195,2736,2737],{},"在信任 Claude 生成的测试前，确认：",[419,2739,2742,2751,2757,2763,2769],{"className":2740},[2741],"contains-task-list",[422,2743,2746,2750],{"className":2744},[2745],"task-list-item",[2747,2748],"input",{"disabled":1492,"type":2749},"checkbox"," 测试是否真的能失败？（修改代码后测试是否报错）",[422,2752,2754,2756],{"className":2753},[2745],[2747,2755],{"disabled":1492,"type":2749}," 测试覆盖了多少变异？（使用 mutation testing 验证）",[422,2758,2760,2762],{"className":2759},[2745],[2747,2761],{"disabled":1492,"type":2749}," 是否有测试是死重？（移除后覆盖率是否不变）",[422,2764,2766,2768],{"className":2765},[2745],[2747,2767],{"disabled":1492,"type":2749}," 端到端流程是否真正连通？（不仅是单元测试通过）",[422,2770,2772,2774],{"className":2771},[2745],[2747,2773],{"disabled":1492,"type":2749}," 错误路径是否被覆盖？（不仅是正常路径）",[199,2776,2777],{"id":2777},"参考来源",[419,2779,2780,2790,2798,2806,2814,2822,2830,2838,2846,2854,2862],{},[422,2781,2782,2789],{},[2783,2784,2788],"a",{"href":2785,"rel":2786},"https:\u002F\u002Fcode.claude.com\u002Fdocs\u002Fen\u002Fcommon-workflows",[2787],"nofollow","Claude Code 官方文档 - 常见工作流"," —— Anthropic 官方测试和调试工作流指南",[422,2791,2792,2797],{},[2783,2793,2796],{"href":2794,"rel":2795},"https:\u002F\u002Fskillsplayground.com\u002Fguides\u002Fclaude-code-testing\u002F",[2787],"Claude Code Testing: TDD, Test Generation & Debugging with AI"," —— 测试生成、TDD 和 Hooks 自动化",[422,2799,2800,2805],{},[2783,2801,2804],{"href":2802,"rel":2803},"https:\u002F\u002Fclaude-world.com\u002Farticles\u002Fclaude-code-tdd-workflow\u002F",[2787],"Claude Code TDD: AI-Assisted Test-Driven Dev Guide"," —— Red-Green-Refactor 完整流程和 Hook 配置",[422,2807,2808,2813],{},[2783,2809,2812],{"href":2810,"rel":2811},"https:\u002F\u002Fwww.eesel.ai\u002Fblog\u002Fdebug-with-claude-code",[2787],"A practical guide to debug with Claude Code in 2025"," —— 调试策略、交互式调试循环和 MCP 集成",[422,2815,2816,2821],{},[2783,2817,2820],{"href":2818,"rel":2819},"https:\u002F\u002Fwww.nathanonn.com\u002Fclaude-code-testing-ralph-loop-verification\u002F",[2787],"Claude Code Testing: How to Make AI Verify (and Fix) Its Own Work"," —— Ralph Loop 自主测试循环",[422,2823,2824,2829],{},[2783,2825,2828],{"href":2826,"rel":2827},"https:\u002F\u002Fgetbeam.dev\u002Fblog\u002Fwrite-tests-with-claude-code.html",[2787],"How to Write Tests with Claude Code: Unit, Integration, and E2E"," —— 测试金字塔各层实践",[422,2831,2832,2837],{},[2783,2833,2836],{"href":2834,"rel":2835},"https:\u002F\u002Fnateross.dev\u002Fblog\u002Fclaude-code-tutorials\u002Fpart-4-fix-bugs-efficiently",[2787],"Fix Bugs Efficiently - Claude Code Tutorials Part 4"," —— 系统性 Bug 诊断和竞态条件案例",[422,2839,2840,2845],{},[2783,2841,2844],{"href":2842,"rel":2843},"https:\u002F\u002Fdev.to\u002Fmyougatheaxo\u002Ftest-driven-development-with-claude-code-write-tests-first-then-make-them-pass-2a6m",[2787],"Test-Driven Development with Claude Code"," —— TDD 核心循环和工厂函数生成",[422,2847,2848,2853],{},[2783,2849,2852],{"href":2850,"rel":2851},"https:\u002F\u002Fwww.ontestautomation.com\u002Fwriting-tests-with-claude-code-part-1-initial-results\u002F",[2787],"Writing tests with Claude Code - initial results"," —— 测试质量评估和变异测试分析",[422,2855,2856,2861],{},[2783,2857,2860],{"href":2858,"rel":2859},"https:\u002F\u002Fshipyard.build\u002Fblog\u002Fe2e-testing-claude-code\u002F",[2787],"E2E Testing with Claude Code"," —— TDD 驱动的 E2E 测试和 CI\u002FCD 集成",[422,2863,2864,2869],{},[2783,2865,2868],{"href":2866,"rel":2867},"https:\u002F\u002Fcollabnix.com\u002Fhow-to-debug-code-with-claude-code-fixing-bugs-10x-faster\u002F",[2787],"How to Debug Code with Claude Code"," —— 调试工作流和 MCP 扩展",[2871,2872,2873],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":399,"searchDepth":566,"depth":668,"links":2875},[2876,2881,2885,2890,2894,2899,2904,2910,2911,2916,2921],{"id":201,"depth":668,"text":202,"children":2877},[2878,2879,2880],{"id":209,"depth":687,"text":210},{"id":378,"depth":687,"text":379},{"id":413,"depth":687,"text":414},{"id":459,"depth":668,"text":460,"children":2882},[2883,2884],{"id":466,"depth":687,"text":467},{"id":538,"depth":687,"text":539},{"id":610,"depth":668,"text":611,"children":2886},[2887,2888,2889],{"id":617,"depth":687,"text":618},{"id":640,"depth":687,"text":641},{"id":858,"depth":687,"text":859},{"id":1276,"depth":668,"text":1277,"children":2891},[2892,2893],{"id":1283,"depth":687,"text":1284},{"id":1696,"depth":687,"text":1697},{"id":1733,"depth":668,"text":1734,"children":2895},[2896,2897,2898],{"id":1740,"depth":687,"text":1741},{"id":1777,"depth":687,"text":1778},{"id":1832,"depth":687,"text":1833},{"id":1873,"depth":668,"text":1874,"children":2900},[2901,2902,2903],{"id":1880,"depth":687,"text":1881},{"id":1950,"depth":687,"text":1951},{"id":1987,"depth":687,"text":1988},{"id":2194,"depth":668,"text":2195,"children":2905},[2906,2907,2908,2909],{"id":2198,"depth":687,"text":2199},{"id":2250,"depth":687,"text":2251},{"id":2263,"depth":687,"text":2264},{"id":2276,"depth":687,"text":2277},{"id":2297,"depth":668,"text":2298},{"id":2549,"depth":668,"text":2550,"children":2912},[2913,2914,2915],{"id":2556,"depth":687,"text":2557},{"id":2589,"depth":687,"text":2590},{"id":2642,"depth":687,"text":2643},{"id":2649,"depth":668,"text":2650,"children":2917},[2918,2919,2920],{"id":2653,"depth":687,"text":2654},{"id":2703,"depth":687,"text":2704},{"id":2733,"depth":687,"text":2734},{"id":2777,"depth":668,"text":2777},"md",null,{"date":2925},"2026-04-26",{"title":90,"description":197},"tWP9xEqV35ca1HJUnKwP6eKbZ0ogXx-GywTlLlhpHMo",[2929,2931],{"title":86,"path":87,"stem":88,"description":2930,"children":-1},"Claude Code 的核心价值在于它能够像一位经验丰富的开发者一样，直接在你的代码库中执行编辑操作。不同于传统的 AI 补全工具只提供建议，Claude Code 使用 Edit 和 Write 工具直接修改文件，并通过代理循环（Agentic Loop）自主规划、执行和验证跨文件的复杂变更。本章将深入探讨如何高效利用这些能力进行单文件编辑、多文件协同修改、代码生成以及编辑审查。",{"title":94,"path":95,"stem":96,"description":2932,"children":-1},"版本控制是软件开发的基石，而 Git 则是这一领域的事实标准。然而，日常的 Git 操作——提交、分支管理、冲突解决、PR 创建——往往占据了开发者大量的心智带宽。Claude Code 将 Git 从一套需要记忆的命令集，转变为可以用自然语言直接操控的协作伙伴。它读取你的仓库状态、理解 diff 的语义、生成符合团队规范的提交信息，并能以对话方式解决合并冲突。本章将系统介绍如何利用 Claude Code 构建高效、低摩擦的 Git 工作流。",1777395308380]