单元测试生成 Prompt:14 个真能抓出 bug 的模板

别再让 AI"给这个文件写测试"。14 个单元测试 Prompt 模板——边界值、错误路径、mock 纪律、参数化、回归锁定。

“给这个文件写单元测试”是最懒的测试生成 Prompt,结果一看就明白:5 条 happy path 断言,错误分支一条没有,不该 mock 的全 mock 了。好的单元测试 Prompt 必须点出边界(null / 空 / 最大 / 负数),禁止过度 mock,并要求至少一条回归测试锁住”行为”而不是”形状”。

适合哪些场景

赶进度但需要真覆盖率的开发、强制测试纪律的 tech lead、给老代码补测试的工程师、用 Claude Code agent 模式的独立开发者。

什么时候不建议这样写 Prompt

别给一次性脚本写测试——维护成本大于收益。也别在不理解代码时让 AI 写测试,否则会把 bug 当成”预期行为”固化下来。

Prompt 结构公式

每个单元测试生成 Prompt 都要带这六个要素:

  • 角色:让 AI 扮演谁(Release Captain / QA Lead / SRE / staff 工程师)。
  • 上下文:仓库 / 框架 / 运行时 / 分支 / diff / 失败日志。
  • 目标:一个具体可交付物——checklist、计划、测试文件、review 笔记、根因、ticket 列表。
  • 限制:AI 不能做什么(别自动修、别静默改写、别瞎猜版本号)。
  • 输出格式:编号清单、markdown 表格、JSON schema、unified diff、可直接运行的代码。
  • 示例 / 信号:1-2 条”好输出”示例,或者说明什么是糟糕输出。

这套 Prompt 适合用在哪

  • AI 写完的新模块,合并前补测试
  • 重构前给老函数补覆盖率
  • bug 修完后写回归测试锁住行为
  • 一组相似输入做参数化表驱动测试
  • toHaveBeenCalled 这种脆弱断言换成行为断言

14 个可直接复制的 Prompt 模板

1. 边界优先测试

永远先跑这条,再跑 happy path。

You are a senior test engineer. For the function `{functionName}` in `{filePath}`, write Jest tests that ONLY cover boundary and error inputs: null, undefined, empty string, empty array, max int, negative, NaN, very long string, malformed input. Do not write any happy-path test in this pass. Each test must have a clear "given / when / then" comment. Use `describe.each` for tables when inputs share a shape.

可替换变量: functionName 函数名, filePath 文件路径

优化建议: 追加:“If a boundary cannot be reached because the function signature forbids it, list it as // unreachable: <reason> instead of inventing a test.”

2. happy path 测试(第二轮)

Now write happy-path Jest tests for `{functionName}`. For each public behaviour described in its docstring or surrounding comments, write one test. Use real values, not generic strings like "test". Use `it("returns X when Y")` naming. Do not duplicate any boundary case already covered.

可替换变量: functionName

3. mock 纪律

Write tests for `{functionName}`. Mock ONLY: (a) network / fs / time / random, (b) external services. Do NOT mock: (a) pure functions in the same package, (b) data classes, (c) anything you could pass as a real value. If you reach for `jest.mock` on a same-package import, stop and use the real implementation instead. Note in a comment which dependencies you chose to mock and why.

可替换变量: functionName

优化建议: Python 把 jest 换成 pytest + monkeypatch / pytest-mock。

4. bug 回归测试

I just fixed this bug: {bugDescription}. The failing input was: {failingInput}. Write a single regression test named `regression: <bug-title>` that fails on the old code (before fix) and passes on the new code. Add a comment with the bug ticket link and the exact behaviour the test locks.

可替换变量: bugDescription bug 描述, failingInput 原始失败输入

5. 参数化表驱动

Convert these example-based tests into a `describe.each` (Jest) / `pytest.mark.parametrize` table. Group by behaviour. Keep one row per distinct equivalence class — don't list 10 rows that all hit the same branch. Name the rows so a failure tells you which case broke.

可替换变量: language:TS / Python / Go

6. 测行为而不是测实现

重构后内部 call site 变了,最适合用这条清理脆弱测试。

Refactor these tests so they assert OUTPUTS / SIDE EFFECTS, not internal calls. Remove any `toHaveBeenCalledWith(internalHelper, …)` assertions. Replace with assertions on the function's return value or the externally observable side effect (DB row, emitted event, file written).

7. 异步 + 计时器

For the async function `{functionName}` that uses `setTimeout` / `setInterval` / `Promise.race`, write Jest tests using `jest.useFakeTimers()`. Cover: (1) resolves before timeout, (2) rejects on timeout, (3) cleanup on abort, (4) no dangling timers after `await`. Avoid real `await new Promise(r => setTimeout(r, …))` — that's flake.

可替换变量: functionName

8. 错误形状契约测试

Write tests that pin the ERROR SHAPE of `{functionName}`. For each documented error path, assert: (a) the thrown class / error code, (b) the message contains a stable phrase callers can match, (c) `cause` is preserved if wrapping. Errors are part of the API — these tests prevent silent breaking changes.

可替换变量: functionName

9. 覆盖率漏洞识别

识别完再跑模板 1 或 2。

Read this test file and the function under test. Without running coverage, identify uncovered branches by inspection. Return a markdown list: branch (file:line) | reason it's missed | suggested test name. Don't write the tests yet — just list the gaps so I can prioritise.

10. fake 替代 mock

Replace these mocks with an in-memory FAKE. The fake should implement the same interface as the real dependency, store state, and let tests assert on that state. Mocks should remain only for I/O at the system boundary (network / DB driver). Show the fake class and 2 example tests that use it.

可替换变量: interface 接口名

11. 属性测试脚手架

For the pure function `{functionName}`, write property-based tests using {framework}. Identify 3 invariants (e.g., idempotence, commutativity, round-trip), and for each, write one property. Use shrinking-friendly generators. Don't test concrete examples — those belong in unit tests.

可替换变量: functionName, framework:fast-check / hypothesis / proptest

12. 测试命名与组织

Reorganise these tests so each `describe` block represents one BEHAVIOUR of `{functionName}`, and each `it` reads as a sentence: `it("returns 0 when input is empty")`. Group setup with `beforeEach` only when ≥ 2 tests share it. Flag tests where the name doesn't match what the body actually asserts.

13. snapshot 策略

Audit these snapshot tests: (1) Which snapshots lock behaviour (good) vs random output / dates / IDs (bad)? (2) Replace bad snapshots with targeted assertions, (3) For React components, snapshot the rendered text + ARIA roles, not the full HTML tree. Output a diff plan.

14. spec → TDD

Here is the spec for `{functionName}`:

{specText}

Write the tests FIRST (TDD style), as a single Jest file, before any implementation. Each test corresponds to one bullet in the spec. Mark unimplemented behaviour with `it.todo`. Do not write any implementation.

可替换变量: functionName, specText

容易踩的坑

  • 只说”写测试”——边界一条都没有。
  • 让 AI 全 mock——最后测的是 mock 不是代码。
  • snapshot 一切——脆且失败时啥都看不出来。
  • 让 AI 同一轮里同时写测试和断言,没有 ground truth——bug 当成预期固化下来。
  • 测试命名 test1/test2——失败时啥也定位不到。
  • 一次让生成 50 条——一堆重复;先边界后 happy path。
  • 跳过错误路径——这是 AI 写的代码最常错的地方。

优化技巧

  • 先边界、再 happy path——分两个 Prompt,输出质量直接提升。
  • 写回归测试时把原始失败输入原文贴进去,别让 AI 编造。
  • 强制要求 // given / when / then 注释,AI 必须先想清楚意图。
  • AI 写完测试先在旧代码上跑一次,能过说明测试就写错了。
  • 用真实 fixture 数据,别用 “foo”,能暴露更多 bug。
  • React 测试要 ARIA role / text,不要 className / DOM 结构。
  • git diff 一起贴进去,AI 才会针对改动写测试。

FAQ

  • AI 写的测试能直接当覆盖率吗?: 不能,先在旧代码上跑一次——如果还能过,就说明根本没在测你以为的东西。
  • 到底要多少条才够?: 边界 + 错误路径 + 每个分支一条 happy path。增加测试不再增加 diff 覆盖率时就停。
  • 老代码该让 AI 直接补测试吗?: 必须先你自己读一遍。否则会把现有的 bug 当成预期行为锁住。
  • 为什么 AI 默认 mock 这么多?: 训练习惯。用模板 3、模板 10 强制改成 fake / 真值。
  • 怎么避免时间 / 随机 / 网络带来的 flaky?: 把这些当依赖注入,测试里用 fake 替换。模板 7 专门讲 timer。
  • Python / Go 适用吗?: 适用,把 Jest 换成 pytest / go test 即可,结构不变。

相关阅读

标签: #Prompt #编程 #测试 #单元测试