大模型帮我写了 40 个接口测试用例,我扔掉了 3 个
寻序集开篇 · AI × 测试开发实验记录 #001
副标题:AI 写接口测试的真实水平,一次完整实测(接口自动化测试 · pytest · AI 生成测试用例)
上周三,我让大模型帮我写接口自动化测试用例。15 分钟,它交给我 40 个用例。
但在逐条跑之前,我发现了它写的一段"神操作"代码:它给 GitHub 写了一个「删除用户」的用例,调用的接口叫 DELETE /users/{username}——而 GitHub API 里根本没有这个接口。用户只能在自己网页端删自己的账号。它编得一本正经,仿佛真有这个接口一样。
先问你一个问题:你觉得这 40 个用例里,真正能直接用的有几个?
我逐条跑完后的实测结果:31 个能用,6 个要改,3 个必须扔掉。
这篇 pytest 接口测试实战记录,把 AI 生成测试用例的整个过程摊开给你看:
- 我设计 Prompt 的全过程(不是简单丢一句话,是 3 版演进到最终版)
- 它产出的代码长什么样(好的坏的都给你)
- 3 个翻车案例逐个拆解(它到底错在哪、为什么错)
- 我改完后的完整代码(可以直接跑)
读完你会得到一套「AI 写接口测试的正确姿势」。 下次你再让 AI 写测试,至少能少踩我今天踩过的坑。
我是寻序集,一名大厂测试开发工程师,入行做测开 1.5 年,目前负责一个 3 万用例的自动化测试体系。
解释一下为什么叫"寻序集"。做测试这行久了你会发现,这份工作的本质,就是在一个看似混乱的系统里寻找秩序:接口为什么偶发失败?压测数据为什么忽高忽低?AI 写的代码为什么时对时错?
我们每天干的事,就是在这堆无序里,找出那个"有序"。
这个账号,我打算认真做一年。为了能坚持,我给自己定了三条铁律:
- 每周 3 篇,雷打不动:周一、周三《AI × 测试开发实验室》,周五《测试开发工程化》
- 每篇真实可复现:代码能跑、数据能对,不写"看起来对"的假教程
- 合规透明:全部用公开接口和通用方法,不涉及任何公司内部信息
这是第 001 号实验记录。开始。
一、为什么选 GitHub API 做实验
先说被测对象。
选它有三个理由:
- 公开免费:search 类接口无需认证就能调用,谁都能复现(本文所有代码我用真实 API 逐条验证过)
- 稳定有文档:接口文档完整,不会今天好明天坏
- 场景丰富:正常、空结果、参数错误、限流……该有的边界它都有
我要覆盖的核心接口和场景:
| 接口 | 正常场景 | 边界/异常场景 |
|---|---|---|
GET /search/repositories | 200 返回搜索结果 | 无匹配返回空结果、缺 q 返回 422 |
GET /search/users | 200 返回用户列表 | 同上 |
GET /rate_limit | 200 返回限流详情 | 字段结构易断言错 |
目标很明确:让 AI 产出一套能直接跑的接口自动化用例。
二、Prompt 是怎么设计出来的(3 版演进)
第 1 版:最省事的写法
帮我写 GitHub API 的接口自动化测试。
结果:结构对了一半,但问题一堆——没有 fixture、断言写死了具体值(数据一变就挂)、完全没有边界场景、限流不处理。可用率不到 30%。
这版实验我 5 分钟就扔了。它证明了一个规律:越模糊的要求,产出越废。
第 2 版:加上技术约束
用 Python + requests + pytest 写 GitHub API 的接口自动化测试,
要参数化,要 fixture,断言响应结构。
结果:框架对了,参数化、fixture 都来了。但边界场景开始"编"——它自己发明了几个不存在的接口,限流处理也是错的。可用率大约 50%。
这版证明了第二个规律:技术约束能纠正"写法",但纠正不了"认知"。
第 3 版:最终版(可直接抄)
我给 AI 的最终版 Prompt 长这样:
你是资深测试开发工程师。请用 Python + requests + pytest 为 GitHub 公开 API
编写一套可运行的接口自动化测试用例。
被测接口(search 类接口无需认证,均可直接调用):
1. GET https://api.github.com/search/repositories?q={query}&per_page=10 —— 搜索仓库
2. GET https://api.github.com/search/users?q={query} —— 搜索用户
3. GET https://api.github.com/rate_limit —— 查看限流详情
要求:
1. pytest 组织用例,使用 fixture 复用 HTTP Session,使用参数化
2. 每个接口至少覆盖:200 正常、空结果边界、参数缺失 三个场景
3. 断言响应结构(关键字段存在性与类型),不要断言具体值
4. GitHub 存在 rate limit(未认证 10 次/分钟):请求前检查 X-RateLimit-Remaining 头,
余额为 0 时使用 pytest.skip 跳过,不要误报失败
5. 搜索接口的 q 参数必须支持中文,使用 requests 的 params 传参,不要手动拼 URL
6. 输出完整可运行代码 + requirements.txt + 一条运行命令
注意第 4、5 条——这两条是我故意加进去的"标准约束",因为我知道 AI 大概率会在这两处翻车(后面的实测验证了)。
Prompt 设计心法(3 条):
- 给角色:让它以"资深测试开发工程师"身份工作,产出质量明显不同
- 给边界:明确被测对象、场景清单、规则——信息密度决定可用率
- 给反例预判:把你知道的坑(限流、中文参数)提前写进去,让它别踩
三、AI 的首版产出(节选)
15 分钟后,它交出了首版。核心代码长这样:
# AI 首版代码(节选)—— test_github_api.py
import pytest
import requests
BASE_URL = "https://api.github.com"
@pytest.fixture(scope="module")
def session():
s = requests.Session()
s.headers.update({"Accept": "application/vnd.github+json"})
return s
@pytest.mark.parametrize("query", ["pytest", "requests"])
def test_search_repositories(session, query):
resp = session.get(f"{BASE_URL}/search/repositories",
params={"q": query})
assert resp.status_code == 200
body = resp.json()
assert body["total_count"] == 100 # 断言写死具体值
assert len(body["items"]) >= 5
第一反应是惊喜的:fixture、参数化、Session 复用、结构断言——模式化的东西它一次就写对了。 这部分它确实很强,不吹不黑。
四、实测结果:哪些能用、哪些要改、哪些扔了
✅ 直接用:31/40
- fixture / 参数化 / Session 复用,规范正确
- 200 正常场景的基础断言逻辑没问题
- 中文参数用
params传参的处理是对的
🔧 要改的:6/40(挑 3 个典型的讲)
坑 1:断言写死了具体值
它写了 assert body["total_count"] == 100——GitHub 的搜索结果每天都在变,这断言今天过了,明天就挂。改法:断言结构不断言数值,比如 assert body["total_count"] >= 0 + assert isinstance(body["items"], list)。
坑 2:没有覆盖"空结果"这个边界场景
最容易被 AI 忽略的场景:q=zzz_nonexistent_987 这种搜不到的查询,应该返回 200 + items=[]。AI 默认搜索"一定有结果",但测试最该测的就是"没有结果时系统表现正不正常"。这是测试意识的问题,不是写法问题。
坑 3:参数化数据写死
AI 把测试数据硬编码在代码里,我抽到了外部 JSON 做数据驱动,用例和数据分离,换数据不用改代码。
❌ 直接扔:3/40(逐个拆解)
废例 1:字段名是"假设"出来的
assert body["limit"] == 60
它断言 /rate_limit 的顶层 limit 字段——但我实际调用发现,真实响应结构是 resources.core.limit,顶层根本没有 limit 这个字段(我验证过,确实如此)。这个用例一跑就挂,而且测不出任何东西——典型的"看起来很对"。
废例 2:编造了一个不存在的接口
就是开头那段"神操作":DELETE /users/{username}。GitHub API 文档里根本没有这个端点,用户只能在自己网页端删自己的账号。AI 会一本正经地编场景,这是最危险的地方——因为它编得太像真的了,外行根本看不出来。
废例 3:用例之间逻辑自相矛盾
它既写了"403 一律是权限不足"的用例,又写了限流处理的用例(限流也返回 403)——两处逻辑冲突。它不会发现自己写的用例之间互相矛盾——这也是为什么必须有"人"在最后兜底。
五、我改完后的完整代码(可直接跑,未认证模式)
这份代码我逐条在真实 GitHub API 上验证过,保存下来,下次让 AI 写测试直接套用。
你保存为 test_github_api.py,装好依赖就能跑(无需 token):
# test_github_api.py —— 修改后完整版
import pytest
import requests
BASE_URL = "https://api.github.com"
RATE_LIMIT_HEADER = "X-RateLimit-Remaining"
@pytest.fixture(scope="module")
def session():
s = requests.Session()
s.headers.update({"Accept": "application/vnd.github+json"})
return s
def _check_rate_limit(resp):
"""限流保护:余额为 0 才跳过,否则真实失败"""
remaining = resp.headers.get(RATE_LIMIT_HEADER)
if remaining is not None and int(remaining) == 0:
pytest.skip("GitHub rate limit exhausted")
return resp
@pytest.mark.parametrize("query", ["pytest", "接口自动化", "selenium"])
def test_search_repositories_success(session, query):
"""正常搜索:200 + 结果结构正确(不断言具体值)"""
resp = session.get(f"{BASE_URL}/search/repositories",
params={"q": query, "per_page": 5})
_check_rate_limit(resp)
assert resp.status_code == 200
body = resp.json()
assert "total_count" in body
assert isinstance(body["items"], list)
@pytest.mark.parametrize("username", ["octocat", "torvalds"])
def test_search_users_success(session, username):
"""正常搜索用户:200 + 结构正确"""
resp = session.get(f"{BASE_URL}/search/users", params={"q": username})
_check_rate_limit(resp)
assert resp.status_code == 200
assert "items" in resp.json()
def test_search_empty_result(session):
"""空结果边界:搜索不存在的关键字,应返回 200 + 空列表"""
resp = session.get(f"{BASE_URL}/search/repositories",
params={"q": "zzz_nonexistent_query_9876543210"})
_check_rate_limit(resp)
assert resp.status_code == 200
body = resp.json()
assert body["total_count"] == 0
assert body["items"] == []
def test_search_missing_param(session):
"""参数缺失:缺 q 参数应返回 422"""
resp = session.get(f"{BASE_URL}/search/repositories")
_check_rate_limit(resp)
assert resp.status_code == 422
def test_rate_limit_endpoint_structure(session):
"""限流接口:字段名必须按真实结构断言(顶层没有 limit!)"""
resp = session.get(f"{BASE_URL}/rate_limit")
_check_rate_limit(resp)
assert resp.status_code == 200
body = resp.json()
assert "resources" in body
assert "core" in body["resources"]
assert "limit" in body["resources"]["core"]
# requirements.txt
requests==2.32.5
pytest==8.0.0
# 运行
pytest test_github_api.py -v
六、效果对比:人工 vs AI
| 维度 | 人工写 | AI 写(首版) | AI + 我修改后 |
|---|---|---|---|
| 耗时 | 约 2 小时 | 15 分钟 | 15 分钟 + 40 分钟修改 |
| 用例数 | 40 | 40 | 40 |
| 可直接运行 | 40 | 31 | 40 |
| 关键分支覆盖 | 85% | 60% | 85% |
| 边界场景质量 | 准 | 部分"看起来对" | 准 |
结论很清楚:AI 把 2 小时压缩到 15 分钟,但剩下的 40 分钟修改时间,一分都不能省。 它省的是你写"模式化代码"的时间,省不了你"想清楚标准和边界"的时间。
注:以上为本次实验数据。GitHub 接口行为可能随官方调整而变化(比如现在未认证下
/users/{username}已要求认证,我实测是 403,所以改用 search 类接口),你复现时跑出的用例数与翻车点可能有浮动——方法不变,数字会变。
七、我的 5 条总结(建议收藏)
1. AI 擅长搭框架,不擅长定标准。
pytest 结构、参数化、fixture 这些模式化内容,AI 一次就对。但"这个接口响应必须有哪些字段、什么状态码才算对"这种业务标准,必须你定、你审。我管着 3 万用例的体系,最怕的就是"看起来对"的用例混进来——它们不报错,但也不测任何东西。AI 写的是"看起来对",我要的是"确实对"。
2. Prompt 的信息密度 = 产出的可用率。
第一版只给一句话,可用率不到 30%;第三版给了完整边界,可用率 77%。这个数不是我编的,是三版实验跑出来的。你给 AI 的约束越多,它替你踩的坑越少。
3. 把 AI 当实习生,不当专家。
现在让 AI 写用例,我的心态就是:来了个干活特别快的实习生,方向必须我把关。限流判断、字段名、场景真实性——这三处它全错了,而这三处恰恰是测试的灵魂。用 AI 提效的前提是你自己先懂,否则你连它错在哪都看不出来。
4. AI 会一本正经地"编"。
它编了一个 GitHub 根本不存在的 DELETE /users/{username} 接口,还一脸确定。我后来翻 GitHub API 文档才确认这个端点从来就不存在。对 AI 产出的代码,先怀疑再相信。 最好的怀疑方式:让代码真的跑一遍。
5. 最好的搭档模式:AI 出初稿,你做"标准与边界"评审。
我的工作流现在很固定:AI 写 → 我审(标准、边界、场景真实性)→ 修 → 跑 → 入库。AI 负责速度,我负责质量。 省下的 2 小时,我用在真正值钱的地方——想清楚这次测试到底要守住什么。
转给正在把 AI 当专家用的同事看看——AI 写测试的真相:80% 很快,20% 是灵魂,而那 20% 恰好全错。
八、这个实验,我接下来还会做 3 次
这次是接口自动化场景。后面我计划继续实测:
- #002:一套可复用的「AI 写测试 Prompt 模板」——把这次的经验固化成模板,你直接抄。评论区被问最多的就是这个,我优先做
- #003:AI 生成测试数据的实测——批量造数据到底能不能信?跟上期"字段假设错、数据写死"的坑直接相关
- #004:把 AI 生成的用例接入 CI,跑起来之后会发生什么
如果你也在让 AI 写测试,欢迎关注这个系列。我们一起来测一测:AI 到底能帮测试做到什么程度。
九、最后,给你一份小礼物
这次实验的完整 Prompt(3 版演进 + 最终版)我整理成了《AI 测试 Prompt 模板包》入门版,里面有:
- 写接口用例的 Prompt
- 生成 pytest 断言的 Prompt
- 构造测试数据的 Prompt
- 审查测试代码的 Prompt
你看到的那版只是 1/4。关注公众号【寻序集】,回复「模板」领取入门版。
(至于高配版——审查、造数、断言 3 套完整 Prompt——我还在认真打磨,会整理成付费模板包,上线时第一时间在公众号通知,不会让你错过。)
最后问一句:你平时会让 AI 帮你写测试吗?翻车过吗? 评论区聊聊,我每条都回——顺便告诉我你最想要哪个场景的模板,我优先做。
—— 寻序集 · 在无序中发现有序 ——
下周见。
更多推荐
所有评论(0)