
之前用 Claude Code 做过一次项目重构代码生成效率确实很高但每次让它补测试时我都要把“用 pytest、写边界条件、函数命名要清晰”这些要求重新叮嘱一遍。时间一长我意识到与其每次重复交代不如把测试生成这件事固化成一套规则文件让 Claude Code 自己学会“看一眼就知道该怎么生成测试”。于是我研究了 Claude Code 的 Skills 机制手写了一个专用于“测试生成”的 SKILL.md。这篇文章会把我的完整做法分享出来从环境安装、SKILL.md 原理到一份可直接复制的测试生成 Skill再到高频报错排查一次讲清楚。无论你是刚开始接触 Claude Code还是已经使用了一段时间想进一步提升效率都应该能从里面找到自己想要的东西。1. 背景Claude Code 与 SKILL.md 是什么1.1 Claude Code 解决了什么问题Claude Code 是 Anthropic 推出的终端编程智能体工具它可以在命令行环境中理解用户的自然语言指令自主完成代码阅读、编辑、调试、测试、修复等一系列开发任务。和传统“聊天框补全代码”的工具不同Claude Code 工作在真实的项目环境中。它能读取文件结构能查看多个文件的上下文能执行测试命令能根据报错信息反复修改代码。换句话说它更像是项目里的一个“结对编程伙伴”而不是一个“增强版自动补全插件”。正因为这样Claude Code 适合处理三类典型场景多文件重构把旧接口改成新接口同时修改调用方和实现方。测试补齐给已有模块补充单元测试和集成测试。技术债清理从报错信息中定位问题反复修改直到测试通过。当然Claude Code 的能力上限并不是由它一个模型决定的还取决于你给它的指令质量、项目上下文和工具配置。这引出了本文的核心通过 SKILL.md 给 Claude Code 注入“领域技能”。1.2 Agent Skills 与 SKILL.md 的关系Skills 是 Claude 生态中的“技能包”机制设计思路是给 Claude 预置一类可复用的专业能力。一个 Skill 本质上是一个文件夹里面最重要的文件就是 SKILL.md。SKILL.md 是 Markdown 格式的技能描述文档包含两部分信息元信息通过 YAML frontmatter 声明技能名称和适用场景Claude 会根据 description 判断何时调用这个技能。技能指令正文中的 Markdown 内容用来逐步引导 Claude 按既定流程完成任务。可以把它理解成“工作手册”。Claude Code 本身是执行者SKILL.md 是方法论的载体。当我想让 Claude Code 具备某些固定工作流时不需要修改它的底层配置只要写一个 SKILL.md 放到约定目录即可。这种设计最大的优势是灵活性。团队里可以把测试规范、代码审查清单、数据库操作守则都各写成一个 SKILL.md按需启用互不干扰。个人项目也可以按语言或框架维护多套技能文件。1.3 为什么要手写“测试生成”Skill很多人觉得 Claude Code 已经足够聪明直接说一句“给 helper.py 写测试”就可以了为什么还要额外写一个 SKILL.md问题在于直接下指令时Claude Code 每次都会自由发挥。它可能这次写 pytest下次写 unittest这次覆盖正常路径下次只测 happy path这次对 Mock 的使用很克制下次却到处打桩。对于个人练手项目这无所谓但在团队项目里测试风格不一致会让维护成本直线上涨。通过 SKILL.md 把测试生成规则固化下来之后好处是明显的生成结果稳定每次生成的测试文件结构、命名、断言风格基本一致。团队规范落地把团队约定写进 SKILL.md等于给每个开发者配了一个“规范翻译器”。减少重复沟通不需要每次重复“请用 pytest、请覆盖边界条件、请用中文注释”。新人友好新成员加入项目时直接读 SKILL.md 就能理解测试应该怎么写。所以这篇文章后面会重点演示如何手写一个“测试生成”SKILL.md让 Claude Code 像装上外挂一样自动按你的规范生成测试代码。2. 环境准备与安装2.1 安装 Claude CodeClaude Code 以命令行工具的形式运行。安装前先确认开发机满足基本条件主要有两件事Node.js 环境。Claude Code 依赖 Node.js 运行时建议使用 18 或更高版本。模型访问权限。要么有 Claude 订阅账号并完成登录授权要么有可用的 API Key / 代理服务配置。安装方式有很多种这里以最常见的 npm 全局安装为例。打开终端执行npm install -g anthropic-ai/claude-code安装完成后在终端执行claude如果是首次启动Claude Code 会引导你完成登录授权流程。不同版本的授权方式可能不同有的是浏览器登录有的是粘贴 API Key请以你实际运行时的提示为准。如果遇到网络问题请检查公司内网代理、系统防火墙等环境限制确保命令行可以正常访问外部服务。如果你希望把 Claude Code 集成到 VS Code 中使用可以安装官方插件。安装后通常会在 IDE 中多出一个终端面板或聊天侧边栏入口使用体验与终端版基本一致。版本说明Claude Code 迭代速度较快本文以当前主流稳定版本的操作逻辑为例不锁定具体版本号。你在实际操作时如果遇到菜单名称、默认路径不一致应优先以当前版本的官方文档为准。2.2 准备一个干净的项目目录老规矩实操之前先准备一个测试项目。不要让 Claude Code 直接跑在你的生产仓库里因为你还不清楚它会产生哪些改动。在本地新建一个演示目录mkdir claude-code-skill-demo cd claude-code-skill-demo在这个目录里我建议先初始化 Git 仓库。这样做的好处是Claude Code 后续修改文件时你可以随时通过git diff查看改动内容不满意时也能一键回滚git init有了这个“后悔药”后面测试 SKILL.md 时就可以大胆尝试了。2.3 了解 Skill 目录与生效范围SKILL.md 放错目录是不会被 Claude Code 发现的。这里涉及两种生效范围个人全局生效放在用户主目录的.claude/skills/下对所有项目生效。项目级生效放在当前项目的.claude/skills/下只对当前项目生效。创建目录的命令如下# 全局技能目录 mkdir -p ~/.claude/skills # 项目技能目录如果你只想在单个项目里启用 mkdir -p .claude/skills正常情况下两种目录可以同时存在。Claude Code 会按“项目级优先”或“合并加载”的策略读取这些技能具体行为请以你当前版本文档为准。对于个人学习场景我建议先使用项目级目录因为改动只影响当前项目不容易出现“全局技能影响了其他项目”的意外情况。3. SKILL.md 核心原理拆解3.1 SKILL.md 的文档结构前面说过SKILL.md 是 Markdown 文件但它的开头有一个特殊的 YAML 配置区叫 frontmatter。一个最小可用的 SKILL.md 看起来是这样的--- name: generate-tests description: 当用户要求为 Python 模块生成 pytest 测试时使用。根据项目现有测试风格输出 pytest 测试文件。 --- # 测试生成技能 你的任务是为指定的 Python 模块生成测试文件。可以看到整个文件只有两大部分frontmatter三根短横线包裹的 YAML 内容声明 name 和 description。正文真正指导 Claude 如何工作的步骤、规范和示例。这个结构看起来简单却是整个 Skill 机制的灵魂。下面分别看每个部分的作用。3.2 Frontmattername 与 description 的作用frontmatter 中最关键的两个字段是name和description。name是技能的内部名称通常是英文短横线风格例如generate-tests、code-review、db-query。这个字段主要用于标识 Skill帮助 Claude 在上下文里区分不同技能。命名没有强约束但建议一眼能看出用途。description决定 Claude 什么时候调用这个技能。这是描述“技能适用场景”的地方。Claude Code 在看到用户请求时会结合当前项目上下文去匹配所有可用技能的 description。如果 description 写得模糊例如“用于写测试”那么当用户说“帮我测一下这段代码”或“写一个 test_demo.py”时Claude 未必会把请求和这个 Skill 关联起来。所以 description 的写法要尽量包含以下信息触发场景什么时候用“当用户要求……”。目标对象作用于什么“为 Python 模块生成……”。输出形式结果长什么样“输出 pytest 测试文件”。一个更精确的写法是description: 当用户要求为 Python 模块编写 pytest 测试、补充单元测试、或检查测试覆盖时使用。输出单文件 pytest 测试命名与模块保持一致。这样触发条件越明确Claude Code 越能准确调用。3.3 正文指令的可执行性SKILL.md 的正文部分是 Claude Code 实际“阅读”的操作手册。它不像程序代码那样一行一行执行而是被 Claude 作为指导性上下文读取。正文写得越清晰、越结构化Claude 在生成文件时的行为就越可控。一段“泛泛而谈”的正文效果通常很有限例如请编写高质量的测试代码注意边界条件和异常情况。问题在于“高质量”“注意”这类词太抽象。Claude 不知道边界条件具体指什么、异常情况要覆盖到哪一层、注释语言用中文还是英文。更好的做法是把步骤拆开1. 先阅读目标模块代码梳理函数列表和输入输出。 2. 为每个公共函数编写测试用例。 3. 每个测试函数命名以 test_ 开头断言必须给出中文失败说明。 4. 至少包含一个正常路径、一个边界条件、一个异常输入。这样 Claude 就知道先做什么、后做什么、结果应该长什么样。本质上写 SKILL.md 正文和写需求文档、写测试用例是相通的指令越具体结果越稳定。3.4 “# 后面的是不是不执行”到底怎么回事在了解 SKILL.md 的过程中很多人会看到类似“skill.md 里面 # 后面的是不是不执行”的讨论。先说结论SKILL.md 是文档不是脚本。Markdown 里的#表示标题它不存在“执行”或“不执行”的语义。Claude Code 读取 SKILL.md 时会把整个文件内容都作为上下文信息交给 Claude 模型。#开头的标题、普通段落、列表、代码块都会被 Claude 一并读取。它的作用不是命令而是组织信息结构让 Claude 更清晰地理解分层关系。之所以有人误以为“# 后面的不执行”可能是因为把 Claude Code 的 Agent 行为理解成了“脚本引擎”。实际上 Claude Code 不会像 Shell 或 Python 那样运行 SKILL.md。它只是在每一轮对话或任务开始前把用户请求、文件上下文、可用技能等信息一起塞进模型输入让模型自行判断怎么处理。所以你可以放心用 Markdown 标题来组织 SKILL.md这不会让指令失效。相反清晰的分层会让 Claude 更准确地抓住重点。真正要注意的是正文里不要写冷冰冰的命令式清单而没有上下文解释。比如只写“必须返回 JSON”是不充分的最好再补一句“返回字段包括 code、message、data其中 data 为测试结果列表”便于 Claude 理解输出结构。4. 手写“测试生成”SKILL.md 完整实战接下来是这篇文章的重点手写一个可用于真实项目的“测试生成”SKILL.md。我会先定义技能边界再给出完整的 SKILL.md 内容最后用一个示例模块做验证。4.1 明确 Skill 的边界动手前先想清楚这个 Skill 要管到什么程度。如果边界太宽Claude 会兼顾太多场景而无法形成稳定风格如果边界太窄实用性又会受限。我给这个 Skill 定的边界是适用语言Python。测试框架pytest。输入一个或多个 Python 模块文件。输出对应的测试文件。强制约定测试函数命名规则、断言风格、中文失败说明、覆盖率参考。这样的边界适合绝大多数 Python 后端项目也容易扩展成 Java/JUnit、JavaScript/Vitest 等风格。4.2 编写 SKILL.md在项目目录下创建技能文件mkdir -p .claude/skills/python-test-generator然后在.claude/skills/python-test-generator/下新建SKILL.md。完整内容如下--- name: python-test-generator description: 当用户要求为 Python 模块生成 pytest 测试、补充单元测试、检查测试覆盖时使用。适用框架为 pytest输出单文件测试命名规范与源模块保持一致。 --- # Python 测试生成技能 你的目标是为 Python 模块生成可直接运行的 pytest 测试文件。 ## 工作步骤 1. 分析目标模块。先阅读模块源码列出所有公共函数和类理解输入参数、返回值和可能的异常。 2. 确定测试文件路径。如果源模块是 order.py测试文件应命名为 test_order.py并放在同级目录的 test 目录或源文件同目录下。 3. 编写测试函数。每个公共函数至少对应一个测试函数函数命名格式为 test_函数名_场景。 4. 覆盖三种场景。 - 正常路径输入合法值断言返回结果符合预期。 - 边界条件空值、极值、长度限制等边界输入。 - 异常输入非法类型、越界值、缺失参数。 5. 运行测试并修复。生成完测试文件后执行 pytest 测试文件路径 -v如果测试失败分析原因并修正测试代码。 ## 编码规范 1. 测试文件使用 pytest 风格函数式用例为主不强制使用类。 2. 每个断言必须附中文提示信息例如 assert result[status] success, 接口未返回成功状态。 3. 对于外部依赖例如数据库连接、HTTP 请求、时间函数默认使用 unittest.mock 进行打桩不依赖真实外部服务。 4. 测试文件顶部要有模块说明注释注明被测模块路径和测试范围。 5. 不修改被测模块的源码。如果测试需要构造复杂对象在测试文件内部提供构造辅助函数。 ## 输出示例 假设被测模块 order.py 中有如下函数 python def calc_total_price(price: float, quantity: int) - float: return price * quantity生成的测试文件应包含import pytest from order import calc_total_price def test_calc_total_price_normal(): result calc_total_price(10.5, 2) assert result 21.0, 正常价格计算失败 def test_calc_total_price_zero_quantity(): result calc_total_price(10.5, 0) assert result 0.0, 数量为 0 时价格应为 0 def test_calc_total_price_negative_price(): with pytest.raises(ValueError): calc_total_price(-1, 1)注意事项如果被测模块存在复杂依赖优先考虑 mock不要跳过测试。如果用户没有明确指定测试目录默认与源模块同目录。如果被测模块包含异步函数测试使用pytest.mark.asyncio确保插件已安装。这份 SKILL.md 包含了很多实用细节。Claude Code 后续只要命中 description 描述的场景就会按这套流程去生成测试。注意我特意在“注意事项”里写了异步函数和 mock 的约定这些在实际项目中很容易踩坑。 ### 4.3 准备被测代码示例 为了验证 Skill 是否生效先在项目里放一个简单的业务模块 order.py python # 文件路径order.py from datetime import datetime def calc_total_price(price: float, quantity: int) - float: 计算订单总价。 Args: price: 单价。 quantity: 数量。 Returns: 总价。 Raises: ValueError: 当价格为负数或数量为负数时抛出。 if price 0: raise ValueError(price 不能为负数) if quantity 0: raise ValueError(quantity 不能为负数) return price * quantity def is_expired(expire_time: datetime, now: datetime None) - bool: 判断某个时间是否已经过期。 now now or datetime.now() return expire_time now这个模块有两个函数一个处理价格计算一个处理过期时间判断。它们都包含正常路径、边界条件和异常情况很适合用来验证 Skill 的执行效果。4.4 安装并触发 Skill不需要额外执行“安装”命令只需要让 Claude Code 能够扫描到技能目录即可。在项目根目录启动 Claude Codeclaude然后输入类似这样的指令请使用 python-test-generator 技能为 order.py 生成测试文件。这里我显式提到了技能名称方便看效果。如果技能加载成功Claude Code 会按 SKILL.md 中的步骤先读取order.py然后生成test_order.py接着尝试运行测试并做修正。更自然的使用方式是只描述需求不直接点名 Skill例如帮 order.py 补一下单元测试用 pytest。如果 description 写得好Claude Code 应当自动匹配到python-test-generator。4.5 验证生成效果Skill 触发后Claude Code 大概率会创建test_order.py。我期望看到的结构是# 文件路径test_order.py order.py 模块的 pytest 测试。 import pytest from datetime import datetime, timedelta from order import calc_total_price, is_expired def test_calc_total_price_normal(): result calc_total_price(10.5, 2) assert result 21.0, 正常价格计算失败 def test_calc_total_price_zero_quantity(): result calc_total_price(10.5, 0) assert result 0.0, 数量为 0 时价格应为 0 def test_calc_total_price_negative_price(): with pytest.raises(ValueError): calc_total_price(-1, 1) def test_calc_total_price_negative_quantity(): with pytest.raises(ValueError): calc_total_price(10.5, -1) def test_is_expired_normal(): expire_time datetime.now() - timedelta(days1) assert is_expired(expire_time) is True, 过期时间应返回 True def test_is_expired_not_expired(): expire_time datetime.now() timedelta(days1) assert is_expired(expire_time) is False, 未过期时间应返回 False从结果可以看出SKILL.md 中“覆盖正常路径、边界条件、异常输入”的要求被落实到了具体用例中而且断言都带了中文提示信息。这就是固化规范的价值不需要你反复叮嘱Claude Code 自动按你写的规则执行。最后在项目里运行测试pytest test_order.py -v如果输出显示全部用例通过说明整条链路已经跑通。如果某些测试失败Claude Code 通常会根据报错自动修复你只需要观察它的改动是否合理即可。5. 让 Skill 更适合真实项目进阶技巧5.1 在 SKILL.md 中内嵌代码模板前面那份 SKILL.md 的“输出示例”部分其实已经起到了模板的作用。但如果你的项目里有很多固定模式的测试可以把这个模式直接写成更完整的模板例如所有 API 测试都用统一的请求封装。一个更高效的做法是把模板放到 Skill 目录下的独立文件中例如templates/test_template.py然后在 SKILL.md 中声明“测试结构以 templates/test_template.py 为模板”。这样 SKILL.md 不会因为模板过长而难以维护Claude Code 也能按需读取模板文件。5.2 多 Skill 组合使用真实项目中一个任务往往需要多个技能配合。例如“为订单模块补测试并生成覆盖率报告”这个任务可能同时涉及“测试生成”和“覆盖率检查”两个技能。在 SKILL.md 的 description 里可以写上交叉触发提示例如description: 当用户要求生成测试、检查测试覆盖率或补充测试用例时使用。但要注意Skill 的组合不是越多越好。如果一个请求同时匹配了多个技能Claude 需要额外判断优先级。建议每个技能只聚焦一个核心职责使用时再通过自然语言组合。5.3 动态上下文注入SKILL.md 是静态文件但它可以要求 Claude Code 在执行时动态读取项目信息。比如你可以在 SKILL.md 中写## 上下文收集 1. 获取当前项目的 pytest.ini 或 pyproject.toml 中的 pytest 配置。 2. 如果项目已有 test 目录先查看目录下已有测试文件的风格。 3. 将被测模块的最近变更记录作为测试重点参考。这样一来Skill 就不是一套死板模板而是能够根据项目现状调整行为的“半自动工作流”。这也是 SKILL.md 进阶使用中很值得尝试的方向。6. 常见问题与排查下表列出使用 Claude Code 和 SKILL.md 过程中比较常见的问题以及对应的排查思路。问题现象常见原因解决思路安装后执行 claude 提示找不到命令npm 全局 bin 目录未加入 PATH检查 Node.js 安装路径把 npm prefix 目录加入 PATH技能一直不生效SKILL.md 放错目录或 description 描述不到位确认放在 ~/.claude/skills/ 或项目 .claude/skills/ 下并检查 description 是否覆盖用户请求场景生成了测试但没有按 SKILL.md 规范执行SKILL.md 正文指令太模糊Claude 自由度太高把步骤拆细增加“输出示例”和“注意事项”让规则可操作测试生成后 pytest 运行失败被测模块依赖外部服务或测试数据构造不正确在 SKILL.md 中明确 mock 策略例如数据库、HTTP 请求默认使用 unittest.mockClaude Code 修改了被测模块代码没有在 SKILL.md 里声明“不修改源码”在 SKILL.md“注意事项”中加入“禁止修改被测模块源码”调用模型时提示模型不认识配置的模型名称与当前版本支持的模型不一致检查模型配置切换到当前版本明确支持的模型名称请求被拒绝提示组织禁用相关访问组织管理后台限制了 Claude Code 的订阅访问联系组织管理员确认权限策略或使用其他有权访问的账号如果你遇到“my skill 好像没被加载”的情况最快的排查方式是确认技能目录名称和 SKILL.md 文件名大小写是否正确。在 Claude Code 中查看当前可用的技能列表确认是否包含你写的技能。直接把技能名称写进提示词例如“请使用 xxx skill”看是否能触发。如果仍然无法触发多观察当前版本的官方文档看看是否有目录约定变化。7. 最佳实践与工程建议7.1 编写 SKILL.md 的基本原则结合我在项目中折腾出来的经验写 SKILL.md 时有几条原则拉高收益第一description 要“窄而准”不要“宽而泛”。每次调用时 Claude Code 都靠 description 判断是否启用技能。描述越贴合用户真实意图命中率越高。第二正文用“步骤 规范 示例”三件套。只有规范没有步骤Claude 不知道从哪里下手只有步骤没有规范输出风格难以统一而示例是最有用的“对齐锚点”能把前两者落回具体形态。第三把“禁止做的事”写进去。例如“禁止修改被测模块源码”“禁止连接真实数据库”。这类约束能有效防止 Claude Code 越权操作对生产项目尤其重要。第四保持单一职责。一个 SKILL.md 只负责一个领域。不要写一个“万能技能”什么都管否则输出质量和稳定性都会下降。7.2 安全边界与权限控制Claude Code 具有修改文件、执行命令的能力所以在工程项目中使用时必须画好安全边界。建议在 SKILL.md 或项目说明中明确测试执行范围只允许在指定虚拟环境或容器中运行 pytest不直接触碰生产环境。文件修改边界只允许生成测试文件和指定目录的文件禁止改动核心业务源码。外部服务访问默认使用 mock 隔离不向真实数据库、第三方接口发起请求。命令执行限制不执行删除、清库等危险命令如有需要必须二次确认。对于权限敏感的场景还可以在操作前让 Claude Code 打印将要修改的文件清单供你人工确认后再继续。这类“确认机制”虽然增加了一步操作但能有效避免自动化工具误操作带来的风险。7.3 团队推广的建议如果你想把 SKILL.md 引入团队不要直接丢给所有人使用建议按以下节奏推进先在个人项目中试用确定技能内容和风险边界。在小团队试点收集反馈尤其是“生成结果是否符合团队测试规范”。把稳定后的 SKILL.md 提交到项目仓库的.claude/skills/目录随代码一起管理。定期更新比如新增 mock 策略、调整测试框架版本、补充新的边界场景参考。SKILL.md 本质上是一份“给 AI 看的团队规范文档”它应该和 README、CONTRIBUTING 一样作为项目资产持续维护。团队成员都可以提出修改建议让这套测试生成规范逐渐演进成团队的统一标准。8. 回到最开始的问题回到开头那个困惑为什么每次让 Claude Code 写测试结果都不一样现在答案已经清楚了。直接下指令等于让 Claude 自由发挥而写一个 SKILL.md 等于给 Claude 一份完整的“测试生成方法论”它会按照你定义的步骤、规范、示例去工作。如果你之前也遇到过“AI 生成测试不可控”的问题不妨试着为你的项目写一个专属的 SKILL.md。先用本文的python-test-generator做模板跑通流程后再一步步加入你项目的特殊约定比如接口测试结构、覆盖率门槛、mock 策略、命名习惯。让 Claude Code 明白你的规范它才能变成真正懂你项目的开发伙伴。下一步你可以继续研究如何把代码审查、SQL 编写、配置文件生成等流程也固化成 Skill把这些重复性工作都交给它去稳定执行。