单元测试aivitestpytest工作流测试
让 AI 帮你写好单元测试:从 0 覆盖到 80%
发布 2026-08-02更新 2026-08-02核实 2026-08-02一句话结论
让 AI 写单元测试,已经从「能生成几行」进化到「能跑到 80% 覆盖」。但前提是给对提示词——无脑「帮我写测试」生成的东西往往只覆盖 happy path、边界缺失、mock 乱飞。用三段式提示词(覆盖目标 → 边界条件 → mock 策略),并让 AI 自查测试质量,效果立竿见影。
三段式提示词
不要只说「给这个函数写测试」。把任务拆成三段喂给 AI:
第 1 段:覆盖目标
给 src/utils/format.ts 里的 formatPrice / formatDate / slugify 写单元测试。
目标:行覆盖 ≥ 90%,每个导出函数至少一个用例。
明确覆盖率数字,AI 才会主动补分支。
第 2 段:边界条件
重点覆盖以下边界:
- formatPrice:0、负数、超大数、undefined、null、NaN
- formatDate:非法日期字符串、时区、闰秒
- slugify:中文、空格、特殊字符、空串
边界是 AI 最容易漏的,显式列出来它才会补。
第 3 段:mock 策略
- 涉及 fetch / 外部 API 的,用 vi.mock 隔离,不要真发请求
- 涉及 Date.now() 的,用 vi.useFakeTimers() 固定时间
- 涉及文件系统的,用临时目录或 mock fs
- 不要为了覆盖率 mock 掉被测函数本身
mock 策略写清楚,生成的测试才「可信」而非「自欺欺人」。
框架适配
三段式结构通用,差异只在断言风格和 runner 命令:
| 框架 | 安装 | 测试文件约定 | 关键 API | 跑命令 |
|---|---|---|---|---|
| Vitest | npm i -D vitest | *.test.ts | describe/it/expect/vi | vitest run |
| Jest | npm i -D jest | *.test.js | describe/test/expect/jest | jest |
| Pytest | pip install pytest | test_*.py | def test_* / assert | pytest |
| Go test | 内置 | *_test.go | func Test* / t.Run | go test ./... |
Vitest 示例(AI 生成后你 review)
import { describe, it, expect, vi, afterEach } from 'vitest'
import { formatPrice } from '../src/utils/format'
describe('formatPrice', () => {
it('正常金额格式化', () => {
expect(formatPrice(1234.5)).toBe('¥1,234.50')
})
it('负数与 0', () => {
expect(formatPrice(0)).toBe('¥0.00')
expect(formatPrice(-5)).toBe('-¥5.00')
})
it('非法输入抛错', () => {
expect(() => formatPrice(NaN)).toThrow()
expect(() => formatPrice(undefined as any)).toThrow()
})
})
Pytest 示例
import pytest
from utils import slugify
def test_slugify_basic():
assert slugify("Hello World") == "hello-world"
def test_slugify_chinese():
assert "python" in slugify("Python 编程")
def test_slugify_empty():
assert slugify("") == ""
Go test 示例
func TestFormatPrice(t *testing.T) {
got := formatPrice(1234.5)
want := "¥1,234.50"
if got != want {
t.Errorf("got %q want %q", got, want)
}
}
让 AI 自查测试质量(review 提示词)
生成后别直接 Accept,让 AI 当 reviewer:
review 刚才生成的测试,检查:
1. 有没有「假覆盖」——只断言不抛错但没验证返回值
2. 边界是否真覆盖(不是只测 happy path)
3. mock 是否过度(mock 掉了被测逻辑本身)
4. 用例之间是否有依赖(必须能独立运行)
给出问题清单并修复。
AIHO 观点:AI 生成的测试最大的陷阱是「假覆盖」——
expect(true).toBe(true)或只断言「不抛异常」。CI 上绿了,其实啥也没测。强制它自查「有没有验证返回值」能过滤掉大半垃圾用例。
覆盖率门槛设置
| 阶段 | 建议门槛 | 理由 |
|---|---|---|
| 新项目起步 | 60% | 别一上来卡太死,先有再说 |
| 稳定模块 | 80% | 核心逻辑达标 |
| 关键路径(支付/权限) | 90%+ | 出错代价高 |
用 vitest --coverage / pytest --cov / go test -cover 看实际数字,逐步提。
CI 上跑通的踩坑
- 测试依赖执行顺序:AI 偶尔写出有状态的测试(共享全局变量),单独跑过、CI 并行跑挂。要求「每个用例独立」。
- 快照测试滥用:AI 爱用
toMatchSnapshot(),但快照一旦生成就永远「通过」。关键逻辑用显式断言。 - 假定时器未清理:
vi.useFakeTimers()后忘了afterEach(vi.useRealTimers),污染下一个测试。 - 覆盖率门槛设太高:新项目直接设 80% 会卡住 CI。建议先 60% 起步,逐步提。
- AI 改了源码来「让测试通过」:review 时注意它是否偷偷放宽了函数逻辑。接 CodeRabbit 在 PR 卡一道质量门更稳。
真实案例:从 0 到 82% 覆盖
一个 2000 行 TS 工具库,原本 0 测试。用三段式提示词分批生成:
- 第 1 批:纯函数(format/parse)→ 覆盖 95%
- 第 2 批:带 IO 的函数(fetch 包装)→ mock 后覆盖 80%
- 第 3 批:边界与异常 → 总覆盖 82%
- AI 自查修掉 6 处「假覆盖」用例
总耗时约 2 小时(含 review),人力约为纯手写的 1/4。
相关阅读
- 工具卡:Cursor | Claude Code | CodeRabbit
- 评测:国产 Copilot 四强横评(单测生成维度)
- 方案:AI 代码评审工作流
- Prompt:测试生成提示词
来源说明:本文基于 Vitest / Pytest / Go testing 官方文档、CodeRabbit 评审能力说明及 AIHO 编辑部实践归纳。框架版本迭代快,命令以官方最新文档为准。