先说痛:AI 写代码最让我睡不着觉的副作用
这个博客的前端、后端、运维、数据库备份、部署脚本全是我个人业余时间维护。我只出需求、出方向、代码全是我让 AI 写的。
最近一年我大量用 AI 帮我改后端。说实话,写起来是真的爽:
- 想加个"管理员能改任意文章"权限?一句话的事,三秒给答案;
- 改某个接口的字段校验?一句话的事;
- 加个错误码?一句话的事;
- 修个"数据库找不到这条数据"没翻译的错误?一句话的事。 但是,AI 改完经常留下"小尾巴" ,这些尾巴拼起来就成大问题:
- 修一个问题,炸出三个新问题。 经典的"我给管理员开了个免校验的绿色通道,结果普通用户也跟着被放行了";经典的"我把空的列表从
null改成了[],但[]又被一个老判断当成 '有数据' 走了另一条路"。 - 新问题不是立刻爆,是过好几个阶段才爆。 比如改了一下文章相关的业务代码,炸的却是评论列表;比如改了下接口的注册顺序,炸的是后台某个我几乎不打开的页面。
- 甚至过了几周我让 AI 写别的功能,才发现"之前好好的地方突然发现不能用了",可距我上次提交代码已经过了半个月之久了。
- 我没空挨个接口都人肉点一遍。 一个人写的小网站,业余时间维护,改完部署上去就不管了,习惯了这种"上线即祈祷"的状态。
所以我需要一套自己能在本地一键跑、能复现、能看错在哪的 e2e 测试。不是为了覆盖率,是为了"敢改" 。
之前用程序跑测试的痛点
在说 skill 之前,先说我之前怎么测:
| 之前的方案 | 痛点 |
|---|---|
| Postman 手点 | 一个接口点 50 遍之后眼瞎;token 过期又得重登;改一个 case 不敢确认其他 case 还活着 |
| 自己写 Python 脚本 | 没规范,想到哪写到哪;跑完一次就扔;换台电脑要重配环境;前后置数据完全靠"我记得" |
| 用现成测试框架(httpx + pytest) | 配测试数据文件一大堆,跑得慢;不熟悉的同事接手直接劝退;我一个人维护没动力写这么多 |
| 让 AI 直接生成测试脚本 | 它会"为了绿而绿":把 expect 改成跟 bug 一样的返回值,让我以为修好了。其实什么都没验 |
| curl + jq 随手测 | 测一个能行,测一个半的,测一个全忘了;下次想回归"上次哪儿改了",根本找不到历史 |
光说 Postman 这条,值得单独多讲两句。我之前是真的用 Postman 当开发工具使的:
每个接口我都"像开发的时候一样",手点 N 遍 —— 增加或减少几个可选字段、增加或减少必选字段、使用正确和错误的数据类型、长度等,反复对一个接口测。
每点一次心里就在嘀咕"这次应该测什么"。点完一遍,我把 Postman 那 N 种尝试全部导出成 curl,想着以后总得写成脚本测试吧。
结果发现根本不可行:
- 导出的 curl 一坨参数,过两天回来看完全不知道在测什么
- 改一个接口的字段,这一堆 curl 全废
- 想"复用"的时候,得一条条对着重新跑,等于又点一遍
- 那个 curl 文件夹最后变成了电子垃圾桶 —— 收了一堆请求,啥也跑不起来
最致命的是"为了全过而全过"。 改完一个问题,再让 AI 改测试,AI 看一眼"哦原来是期望 200",直接把 50000 改成 200,你就得到一份"全过但其实没验任何东西"的报告。
这几种方案,都解决不了"一个人 + 业余时间 + 持续维护"这个真实场景。
为什么用 skill
Skill 是"角色插件":我写一份 markdown 告诉它"你是测试编排员,你怎么跑、怎么判断、怎么出报告",然后用一个 /api-test 命令就能召唤它。
我选 skill 路线,是因为它长在我已经习惯的工作流里:
- 我本来就每天都要打开IDE或Claude Code
- 它能直接读
cases.yaml、跑run.sh、看report.jsonl、告诉我"第几条为什么没过" - 它不会被"为了全过而全过"骗到——因为 skill 文件里写死了硬规则,比如"不修改后端代码"、"不破坏 state.json"、"失败信息要带原始请求和返回"
- 我可以用"前一次跑出来的失败"作为上下文,让 AI 帮我只改后端、不动测试,反推出问题在哪
换句话说:skill 把"测试怎么跑"和"测试怎么写"这两件事都变成可版本控制的纯文本。我一个人也能 hold 住。
为什么是 skill,不是 pytest
我不是没试过 e2e 测试。去年就折腾过一轮,目标很朴素:
启一个空数据库,自动注册一个账号 → 登录拿到登录凭证 → 带这个凭证去增加各种数据,再测所有接口的增删改查。
听起来简单。实际做起来,几个卡点每个都致命:
- 注册→登录→拿凭证的链路 —— 每一步得先跑通才能进下一步,失败一次后面全白搭
- 接口之间有依赖关系 —— 要测发文章,得先有分类和标签;要测评论,得先有文章;要测删分类,得先有一个用着它的文章。几十个接口拧成一根链,顺序错一个就崩
- 每个接口还得测反例 —— 错密码、过期凭证、不存在的 ID、跨用户权限、字段超长,排列组合一上来就成百上千条
- 测试脚本最后自己就是个 DSL —— setup → 主流程 → 反例 → 清理,自己写完自己都晕
去年我让 AI 帮我"整理"这个测试脚本。AI 听完,讲了一套 pytest 教科书:fixture、conftest、parametrize、hook、marker……头头是道。我照着写,很快就放弃了。
问题在哪?AI 给的方案是 "教科书正确",但在我这个具体项目里就是跑不起来。要么 fixture 互相引用打死结,要么参数化跟我的接口形态对不上,要么脚本顺序和业务顺序冲突,要么某个 hook 吃掉我的错误码。
AI 嘴上头头是道,跑起来一塌糊涂。
这事我放下了。直到最近两天,我一直在想一个问题:能不能让 AI 不再"给我写测试",而是"AI 亲自当测试员"? 不是 AI 给我一段脚本我去跑,而是 AI 自己跑测试、看到红色自己知道怎么办。
脑洞就来了:skill 机制。把"测试怎么跑、跑出来怎么判断、错了怎么办"全写在一个 markdown 文件里,我每次输 /api-test,AI 就被召唤成"测试编排员",按这份文件干活,亲自跑、亲自看、亲自跟我说"这条红的可能是 X"。
这就是 api-test skill 的诞生。
skill 凭什么能跑通我之前跑不通的
跟 pytest 比,skill 没多出啥"超能力"。根本差别就一句话:AI 不再是"给我一段脚本我去跑",而是"AI 自己在跑测试" 。
具体表现就是,之前那几个卡点一个个被解掉:
| 之前的卡点 | pytest 那套 | skill 模式 |
|---|---|---|
| 怎么启空环境 | 我得自己写 docker-compose、写清理脚本 | skill 里有 mode.reset_db 一行开关,清表 + 重迁 + 初始化管理员一条龙 |
| 注册→登录→拿凭证 | 我得自己写一长串 setup 函数,失败回滚还得手写 | YAML 里写 auth: u1 就完事,AI 自己去找之前用例保存的 token |
| 接口之间的顺序 | 我自己排 fixture 依赖,排着排着就乱 | AI 看着 cases.yaml 帮我排;我加一条用例,AI 自己挑阶段、挑位置、决定要不要先建前置数据 |
| 每个接口的反例 | 我自己 parametrize 一通,组合爆炸 | 我对 AI 说"加测 X 的边界",AI 自己在 YAML 里写 5 条反例 |
| 脚本顺序我自己晕 | 测试代码写到几百行就劝退 | YAML 几百行,排版好,我自己一眼能看完 |
| 跑挂了谁解释 | 我自己读堆栈、查日志、看堆 | AI 直接读报告,告诉我"这条红的可能是 X 字段没赋值" |
| 怎么增量跑(只跑改动的) | 我得自己写 marker、配置 pytest | 我直接说"先只跑文章相关的",AI 改 cases.yaml 加个 filter,跑完恢复 |
最值钱的一行,是最后那一行:测试代码几百行,我自己看就晕;YAML 几百行,我一眼能看完。 skill 不让 AI 替我"写测试",它让 AI 替我"跑测试 + 解读结果 + 提醒我哪里要改后端"。
对我这种"一个人 + 业余时间 + 主要让 AI 写代码"的人来说,这是质的区别: 我不用额外抽时间"维护测试",测试是被 AI 改后端的那个 AI 顺手维护的。
第一次跑通:证明思路可行
最先的版本非常朴素,目标就是一句话: "返回的业务码对就算过。"
- 跑一个
bash test/run.sh - 它做这些事:编译后端 → 启一个独立端口(8081,跟开发的 8080 隔开)→ 等服务正常启动 → 清空所有数据表(管理员账户保留)→ 跑用例 → 出一份网页报告 → 关掉后端
- 每个用例写在一个 YAML 文件里,带
expect.code(期望业务码)和expect.msg_contains(期望错误信息里要包含的那句话) - 跑出来的卡片直接列出"我发了什么请求 / 后端回了什么 / 业务码是多少",没过的话一目了然
第一版我让 AI 写了一百多个主流程用例。大概 100 个左右,核心 11 个阶段:环境就绪、注册、登录、当前用户、分类、标签、文章增删改查、评论、单页面、上传、错误处理。
写完一跑,100% 全过。这其实一点都不奇怪——我是按"应有行为"写的期望,如果按"迁就现状"写,那 100% 是廉价的。我特意没迁就,就是想看看真实后端会暴露什么。
这次跑通证明了一件事:这个思路行得通。 一键能跑、能复现、能出报告。哪怕只有 100 个用例,也比"改完部署上线就靠运气"强 100 倍。
下面这张图是整个工作流的全貌,后面会按这张图一个个展开讲:

(图:AI 改后端 → 触发 /api-test → 自动跑测试 → 看红绿报告 → 红的让 AI 修,绿的安心部署;以及 4 个阶段严格度一路加码)
第一阶段:让 AI 写测试,覆盖所有接口
跑通之后,我做了一件"反直觉"的事:让 AI 帮我写更多 case。
为什么反直觉?因为按我之前的经验,AI 写测试不可靠。
但这次不一样——skill 里写死了一条硬规矩: "测试不主动改后端代码" 。也就是说,AI 写测试时,它没法靠改后端来"实现"它的期望。它只能老老实实写"应该有",然后跑出来没过的话,问题归后端管。
所以我让 AI 一口气把后端所有接口全覆盖了。结果:
- 11 个阶段 → 不变
- 用例总数从 100 涨到 217
- 一跑出来,立刻暴露出后端一堆问题(后面会讲)
这一阶段的核心:返回的业务码正确就算过。code: 200 算过,code: 50000 算挂,msg_contains 是模糊匹配一下错误信息里有没有指定的字。
最直接的成果:我把后端 60 多个接口的"应有业务码"全部钉死了。再没人(包括我自己)能用"我以为这个返 200"来糊弄过去。
第二阶段:响应的结构也得对
跑了一阵,发现一个尴尬事:业务码对了,数据是错的,测试也显示通过。
比如:
GET /users/current返code: 200,但data.user.username不是user1,是usernme(AI 改字段名时拼错);POST /articles返code: 200,但data.article.id是字符串而不是数字;GET /tags返code: 200,但data整个是null,因为空表时空切片被序列化成了 null。
业务码说"过",其实过的是空壳。
所以加了 expect.data 这个字段:
yamlexpect: code: 200 data: type: object fields: - path: $.data.token type: string not_empty: true - path: $.data.user.username type: string eq: user1 - path: $.data.tags type: array min_len: 1
规则很轻量:type(类型) / eq(等于) / oneof(在某个白名单里) / contains(包含) / not_empty(非空) / min_len(最少多少) / max_len(最多多少) / matches(正则匹配),fields 一层层下钻。

我让 AI 把现有 217 个用例全部补上 expect.data,然后跑。
结果:通过率从 100% 直接掉到 84.8%(184/217)。
没过的那 33 个,全是真问题。最经典的几个:
- 空切片被序列化成
null(前端每个数据读取的钩子都要加?? []兜底) - "数据库找不到这条数据"没翻译,直接 50000
- 字段名拼写错误
这一阶段的"红利"是:风格债被钉死了。 "序列化一致性"从一个"大家默认遵守"的口头约定,变成了测试报告里明明白白的是/否。从此再没人能"忘了"加 ?? []。
第三阶段:字段类型也得对
加了 expect.data 之后,马上又发现一个层次的问题。
假设:测试用例写的是"等于 1",后端返的也是 1,显示通过。但按 JSON 接口规范,****1 应该是数字,不能是字符串。
而代码那边稍不留神就会把数字序列化成 "1"(比如某些字段被设成字符串,或者经过文本格式化)。
所以又加了 type: number 强制约束,光值一样还不够,类型也得对。

类似的还有:
data.user.role必须是"user"或"admin"之一,我加oneofdata.created_at必须匹配日期格式,我加matches(正则)data.articles是数组时,每个元素都得有id/title/status三个字段,我加array_items
这一轮又是暴雷。AI 改完几个用例之后,又冒出几个"业务码对 + 结构对 + 单字段值对 + 字段类型错"的微妙问题。
第四阶段:副作用产生的数据量也得对
到这里,我以为差不多了。
然后有一天我好奇:"我注册了两个用户,登录拿登录凭证,写了一篇文章,加了三条评论,删了一篇文章——列表里应该有 2 个用户、1 篇文章、3 条评论,数据量真的对吗? "
我加了一组列表接口的"最少多少 / 最多多少"断言,要求"我刚刚操作完,列表长度就该是多少"。
结果立刻不过。原因五花八门:
- 删除一个用户,关联的评论没连带删,列表里残留"幽灵评论"
- 被标记删除的文章还出现在公开列表里(假删没真删干净)
- 翻页参数
?page=0真的返第 0 页(2 条),而不是回退到第 1 页 ?limit=0真的返 0 条(空响应),而不是用默认值- 被标记删除的实体在关联查询里被算两次
这一阶段的"红利"最大:测试开始验证"业务流程的副作用",而不只是"单次接口的返回值" 。它把"我能不能信任这个后端"从"单个接口看起来对"升级到"业务流程对得上"。
跑完报告,我又顺手让 AI 把 bug 修了
到这里你以为流程结束了?其实真正的闭环在报告跑完之后。
测试报告一出来,我直接对 AI 说一句: "把报告里红的那几条,逐条帮我看看 —— 是不是真 bug,如果是,直接改后端,改完跟我说改了哪些文件。"
然后 AI 会按这几步走:
- 逐条读红的用例 —— 看报告里的
request / response / 业务码,判断到底是谁的锅 - 区分"测试期望有误"和"后端真有 bug" —— 如果是前者,AI 主动停下来跟我确认;"是不是期望写错了?"
- 确认是后端 bug → AI 只改后端代码,绝不碰
cases.yaml(这一条是写死在 skill 里的硬规矩) - 改完给我看 diff —— 改了哪几个文件、哪几行,一句话讲清楚改了什么
我做什么?只看 diff,审一眼改得合不合理、有没有把测试代码偷偷改了、修改范围有没有扩大。 没问题,一句"提交",AI 帮我生成提交信息,推上去。
这是我能两天修 30 多个 bug 的真正原因 —— 不是熬夜,是"修"这个动作也被 AI 顺手自动化了。 我在前面写"反向促进 API 完善"那一节里列的 4 次修复提交,基本就是这个流程跑出来的:
- 跑一次测试 → 报告出来 N 条红的
- 让 AI 修 → AI 改完给我 diff
- 我审 → 通过
- 再跑一次测试 → 报告里少一些红,多了几个新红(也可能是 AI 改出来的)
- 继续让 AI 修
- 直到全过
这个循环完全在我下班前的 1~2 个小时里完成,每次循环大概 20~40 分钟。我做的实际工作只剩两件:审 diff + 偶尔判断"这条是不是测试期望写错了,而不是后端的锅" 。剩下全是 AI 在干。
这种"测试 + 修 bug"打包在一起的工作流,在我之前的方案里是完全不可能的:Postman 时代我得自己看返回、人肉定位、自己改代码、自己 commit;pytest 时代我得自己读堆栈、自己定位、自己改代码。skill 把它打包成一个对话回合,我只需要在最关键的两个判断点(期望有误 vs 后端 bug + diff 是否合理)露脸。
反向促进 API 完善:这两天我修了多少个 bug
写到这里,真正核心的问题来了:AI 帮我改问题容易,AI 帮我写测试反过来逼我修问题,反而是更值钱的产出。
我把这两天的版本记录翻了一遍(只统计后端的代码改动),给自己也算笔账:
整体规模
- 6 次提交,18 个后端源文件被改过
- 新增代码 4500 多行,删除 1700 多行
- 4 次是修复,1 次是结构重构,1 次是新增功能(测试模式)
按提交拆,4 次修复一共解决了 30 多个具体问题
第 1 次修复(深夜) 涵盖:
- "数据库找不到这条数据"统一识别(影响查分类/标签/文章详情等多个接口)
- 接口注册顺序调整(静态接口
/top不再被动态的/:id抢走) - 文章修改增加管理员绿色通道,与删除对齐
- 文章访问量排行排序字段名
views改成view_count(之前一直是 500) - 标签删除前先检查存不存在 + "正在被 N 篇文章使用"统一错误码
- 用户不存在统一错误码
- 401 鉴权失败改用统一响应格式(不再裸 HTTP 码)
- 搜索的全文索引在 SQLite/MySQL 行为差异
- 已知错误文案自动替换脚本
{{N 个字符}}占位符支持(测最大长度边界)run.sh跨平台关进程 + 文件路径兼容
第 2 次修复(上午) 涵盖:
- 全局把空切片从
null改成[](前面提到的"风格债") - 分类/标签创建拒绝纯空白名
- 注册时检查邮箱是否重复
GET /users/current改用登录凭证上下文拿 ID(原本写死了 1,会撞死)- 验证码测试模式可绕过
- 请求结构补字段长度校验:
description最长 200、content最长 500、guest_name最长 50 - 业务校验错误和服务器错误分开(不再全 50000)
- 错误码文案统一中文
第 3 次修复(上午晚些) 涵盖:
- 登录按错误信息返回对应错误码(密码错和用户不存在分开)
- 新增"被封禁"状态检查,被封禁用户拒绝登录
- 文章排序字段
views改成view_count cases.yaml一次扩 408 行,补用户状态/业务流程
第 1 次重构(凌晨) 涵盖:
- 新建
BaseModel替代老的gorm.Model+ 全部 model 加json标签(序列化字段名统一) - 序列化时空切片不再变
null expect.data断言支持(对应前面第二阶段)- 网页报告强化 data 校验展示
- 忽略文件调整
几个让我印象深的"测出来的问题"
| 我永远不会主动想到的 | 测试怎么揪出来的 |
|---|---|
| 删一个用户,关联评论没连带删 | 阶段 13 加"最少多少"之后,数字对不上 |
老的 gorm.Model 字段没 json 标签,JSON 返大写 key | 我以为只要业务码对就过了,加"类型是对象"验字段名,立刻炸 |
?page=0 真的返第 0 页(2 条) | 阶段 11 加"翻页边界",按"应有"写,后端没回退 |
| 注册新用户时,邮箱是否重复完全没查 | 加"等于 user1@test.local"验新注册,验完发现邮箱能撞 |
| 被封禁用户还能用旧登录凭证继续请求 | 阶段 13 加"登录凭证边界",封禁后用旧凭证仍 200 |
view_count 字段名写错,排行接口一直 500 | 期望业务码 200 的用例不过,改期望之前先抓响应发现是 500 |
data: null 反复出现 | 一加"类型是数组",空表场景 100% 不过 |
这其中有至少 3 个是"修问题改出来的问题", 我自己永远不会回头去测:接口注册顺序、字段名拼写、假删的关联查询行为。没有这套测试,我到下次自己撞上才会发现。
Skill 自己也不是银弹:几个我自己也头疼的痛点
聊完好处,得诚实说说 skill 现在的局限:
1. AI 写测试还是会"为了全过而全过"。 我得在 skill 里反复写硬规则提醒它"不许改期望来迁就现状",它偶尔还是会忘。唯一保险的办法是我自己回头看。 我现在每加一组用例,跑一次,看没过的那批是不是真的在抓问题,而不是"全过但其实没验"。
2. 跨阶段顺序敏感。 我有一条用例是"删使用中的分类,期望 3003",但只有 art1 引用了 tech 之后,这条用例才能拿到 3003;如果 art1 还没建,它就返 200。我的"应有"写法和实际跑出来的状态对不上,就得在 note: 里写明"前置依赖 art1 已建"。这种"隐性知识"藏在 YAML 的顺序里,新人接手根本看不出来。
3. state.json 是会咬人的。 跑完"改密码"那组用例,u1 旧登录凭证还活着,后续依赖 u1 旧密码的用例直接被跳掉一片。我 debug 半天才发现。所以"先按生命周期、最后跑破坏性的用例"是硬规则,但这条规则没强约束,只能靠我自律。
4. 上传、限流、MySQL 特性测不到。 文件上传要准备测试数据文件、限流要调白名单、MySQL 的全文索引 / 事务隔离 / 字符集我用 SQLite 跑不到。这些边界我心里有数,但 skill 目前覆盖不了。
5. 持续集成没接上。 提一句,因为我用的编译镜像没装 sqlite3,跑测试要改服务配置 / 镜像选择 / 阶段顺序 / 与编译并行,得另开一仗。
现在它长什么样
写完这篇的时候,我手头的最终状态:

- 300 个用例 / 14 个阶段
- 100% 都带
expect.data断言(报告里 "data 校验" 一栏是 300/300) - 跑了 91 次,1 秒级完成
- 终端 + 网页双报告,网页版有粘性顶部可以跳阶段、按关键字搜
cases.yaml 里有个 note: 字段专门记"现状/已修",目前 34 条。这 34 条是测试帮我"看见"的后端缺陷清单,每修一条划一条。
Bash# 一键跑 $ bash test/run.sh [build] 编译后端到 test/bin/server [server] 启动 (port 8081) [ready] 等后端就绪 OK (500ms) [clean] 清 7 张表 [run] test/run_tests.py [report] test/build_html_report.py --real [done] exit=0 ✓ 环境就绪 2/2 ✓ 用户注册 13/13 ... 300 总数 300 ✓ 通过 0 ✗ 失败 0 ? 跳过 整体通过率 100.0% 用时 1.0s
写在最后
这套东西不是一次写完的。我分了 4 个阶段,每阶段要求都更严:
- 业务码对就算过(217 个用例)
- 响应的结构也得对(引入
expect.data,通过率掉到 84.8%) - 字段类型也得对(数组元素、白名单、正则)
- 副作用产生的数据量也得对(列表长度、连带删、假删)
每加一层,测试就逼着我修一批问题。这两天 4 次修复提交、30 多个修复点,就是这个过程的产出。
最大的收获不是"300 个用例全过",是"今天我敢放心让 AI 改后端"。 改一行,跑一遍;没过的让 AI 改,过了再部署。这种安心,业余时间维护个人项目的人最懂。
由于这个skill测试用时很短,几秒就完成,以后我甚至可以在修改这个项目的时候,提交前把这个skill跑一遍,没问题再提交。
如果你也是一个人写后端,被 AI 写代码的速度爽到、被它留下的问题吓到,试试这套思路:让 AI 帮你写测试,而不是让 AI 改你的期望。 跑出来没过的归后端管,过了的归期望管。测试永远比后端诚实,这是它值钱的地方。
附录:关键数据
| 指标 | 数值 |
|---|---|
| 用例总数 | 300(初版 100,扩到 217,再扩到 300) |
| 阶段总数 | 14(初版 11,扩阶段 11/12/13) |
| 响应数据断言覆盖 | 100% |
| 历史报告 | 91 份网页报告(累积保留) |
| 两天内的接口改动 | 6 次提交,18 个后端文件,新增 4500 多行 / 删除 1700 多行 |
| 修过的问题点数 | 30 多个(4 次修复提交 + 1 次结构重构) |
cases.yaml 里 note: 字段(已知现状) | 34 条 |
| 跨平台 | Linux / WSL / Git Bash on Windows |
| 一键命令 | bash test/run.sh(或在 AI 编程助手里输 /api-test) |
