1. 项目缘起:为什么要用 AI 来设计测试用例
做测试这行的朋友大概都有体会,写测试用例是个既费脑子又费时间的活儿。一个中等规模的功能模块,边界条件、异常分支、组合场景全捋一遍,半天就没了。更头疼的是,人写用例天然有盲区——你脑子里默认“这个字段用户肯定会填对”,结果上线后偏偏有人传了个空字符串,服务直接 500。这类问题靠人盯,盯不过来。
我这个项目就是冲着这个痛点去的:用 AI 辅助生成测试用例,再借助自动化手段把隐藏 Bug 挖出来。核心思路不复杂——把需求描述、接口定义、数据约束这些信息喂给大模型,让它批量产出覆盖边界和异常的用例,然后我用 Playwright 把其中可自动化的部分跑起来,最后人工复核 AI 给出的“可疑点”。整套流程走下来,原本需要大半天的用例设计,压缩到一两个小时,而且覆盖度反而更高。
适合谁来参考?如果你是有一定测试基础、想提升效率的测试工程师,或者做后端/前端开发、需要自己保证代码质量的程序员,这套方法都能直接抄。哪怕你之前没怎么用过 AI 编程工具,跟着走一遍也能上手。我不打算讲什么高深理论,就按我实际操作的顺序,把每一步为什么这么做、踩了哪些坑,原原本本说清楚。
需要先说明一点:AI 不是替你思考,它是替你“发散”。它能想到你没想到的边界,但它也会一本正经地胡说八道。所以这套流程里,人工复核永远是最后一环,不能省。
2. 整体方案设计:AI 生成 + 自动化执行 + 人工复核
2.1 为什么选“AI 生成 + Playwright 执行”这条路线
市面上测试用例生成的路子大致有三种:纯手工、基于规则的模板生成、以及大模型生成。纯手工的问题前面说了,慢且有盲区。基于规则的模板生成(比如根据参数类型自动套边界值)速度快,但它只能处理结构化很强的场景,遇到“用户先领券再下单、下单时券过期了怎么办”这种业务逻辑组合,模板就抓瞎了。
大模型的优势恰恰在业务语义理解上。你把一段需求文字丢给它,它能读懂“优惠券过期”和“下单”之间的时序关系,进而生成对应的用例。这是规则引擎做不到的。所以我选大模型做“用例发散器”。
那为什么执行环节选 Playwright 而不是别的?几个实际考量:第一,它同时支持 Chromium、Firefox、WebKit,一套脚本多浏览器跑,省事;第二,它的自动等待机制做得比较扎实,写起来不用到处sleep,脚本稳定性高;第三,它对 API 测试的支持也够用,request上下文可以直接发 HTTP 请求,UI 和接口用例能放一个工程里管。相比之下,Selenium 配置繁琐,Cypress 对多标签页和跨域支持一般。当然这是基于我自己的使用习惯,你用别的工具也完全可行,思路是通用的。
2.2 整套流程的四个阶段
我把整个项目拆成四步,后面每个章节会展开讲:
- 信息准备:把需求、接口文档、数据字典整理成 AI 能吃的格式。
- AI 生成用例:设计提示词,让模型输出结构化的用例清单。
- 自动化落地:挑出可自动化的用例,用 Playwright 写成脚本跑起来。
- 结果复核与 Bug 定位:分析失败用例,区分“真 Bug”和“用例写错了”。
这里有个关键决策点值得说:不要指望 AI 一次性输出能直接跑的代码。我试过让模型直接生成 Playwright 脚本,结果选择器全是它编的,page.click('#submit-btn')里的 ID 根本不存在。后来我改成让 AI 只输出“用例描述 + 预期结果”的结构化文本,脚本我自己写,效率反而高得多。AI 负责想“测什么”,我负责写“怎么测”,分工明确。
2.3 工具选型清单
| 环节 | 工具 | 用途 | 备注 |
|---|---|---|---|
| 用例生成 | 通用大模型对话工具 | 根据需求发散用例 | 选支持长上下文、逻辑推理强的 |
| 脚本编写 | Playwright + Python | UI/接口自动化 | Python 版本生态成熟 |
| 代码辅助 | 编辑器 AI 插件 | 补全脚本、解释报错 | 选响应快、不干扰编码的 |
| 用例管理 | Markdown 表格 / Excel | 记录用例与执行结果 | 轻量项目够用 |
| 缺陷记录 | 任意 issue 工具 | 跟踪发现的 Bug | 按团队习惯来 |
提示:工具只是载体,别在选型上纠结太久。我见过有人花两周对比测试平台,结果一个用例没写。先用起来,不合适再换。
3. 核心细节:怎么让 AI 生成高质量的测试用例
3.1 喂给 AI 的信息要“结构化”
很多人用 AI 生成用例效果差,问题出在输入太随意。你丢一句“帮我测一下登录功能”,它只能给你“输入正确账号密码能登录、输入错误密码不能登录”这种废话。要让输出有价值,输入必须包含三类信息:
- 功能描述:这个功能是干什么的,正常流程是什么。
- 数据约束:每个字段的类型、长度、必填性、取值范围。
- 业务规则:字段之间的依赖关系、状态流转、权限限制。
我一般整理成下面这种格式再喂给模型:
功能:用户登录 正常流程:输入手机号 + 密码,点击登录,跳转首页 字段约束: - 手机号:11位数字,必填,需为已注册号码 - 密码:6-20位,必填,区分大小写 业务规则: - 连续5次密码错误,账号锁定30分钟 - 未注册手机号提示"账号不存在" - 账号锁定期间即使密码正确也拒绝登录这样喂进去,AI 生成的用例质量立刻上一个台阶。它会主动覆盖“锁定期间用正确密码登录”这种组合场景,而不是只盯着单个字段。
3.2 提示词设计的三个要点
提示词(prompt)写得好不好,直接决定输出质量。我总结下来有三个要点:
第一,明确角色和输出格式。开头就告诉它“你是一名资深测试工程师”,然后规定输出必须是表格,列包含“用例编号、用例标题、前置条件、操作步骤、预期结果、优先级”。格式定死了,后面处理起来才方便。
第二,要求覆盖特定维度。我会明确列出希望它覆盖的维度:正常流程、边界值、异常输入、业务规则组合、并发/时序。不列出来,它容易只给正常流程。
第三,要求标注“不确定项”。这一点很多人忽略。我会加一句:“如果某条用例的预期结果你无法从给定信息中确定,请标注‘需人工确认’并说明原因。”这样 AI 不会硬编一个预期结果骗你,反而能帮你发现需求文档里没写清楚的地方。
3.3 一个实际使用的提示词模板
下面是我反复调整后比较顺手的模板,你可以直接拿去改:
你是一名资深测试工程师,请根据以下功能信息设计测试用例。 【功能信息】 (粘贴整理好的功能描述、字段约束、业务规则) 【输出要求】 1. 以 Markdown 表格输出,列为:编号 | 用例标题 | 前置条件 | 操作步骤 | 预期结果 | 优先级 2. 必须覆盖以下维度:正常流程、边界值、异常输入、业务规则组合、状态时序 3. 优先级用 P0/P1/P2 标注,P0 为核心流程 4. 若预期结果无法确定,标注"需人工确认"并说明原因 5. 用例数量不少于 20 条实测下来,这个模板生成的用例,覆盖度能到人工的七八成,剩下的靠人工补充和调整。
3.4 注意事项:AI 生成用例的三个坑
坑一:AI 会编造不存在的规则。有次我让它测一个订单功能,它自作主张加了“订单金额超过 1 万需要人工审核”这条用例,可我们的系统根本没这规则。所以拿到输出后,每一条涉及业务规则的用例都要对照需求文档核实。
坑二:边界值容易给错。比如字段限制是“6-20位”,AI 有时会生成“5位”和“21位”作为边界,这没错,但它偶尔会漏掉“6位”和“20位”这两个正好在边界上的有效值。我一般会手动补上。
坑三:重复用例。生成 30 条,可能有 5 条本质是同一个场景换了措辞。去重这步得人工做,或者让 AI 自己再跑一遍“合并重复用例”。
4. 实操过程:从用例到自动化脚本的完整落地
4.1 第一步:整理需求并生成用例
我拿一个真实的“优惠券领取”功能来演示。需求是这样的:用户可以在活动页领取优惠券,每人限领一张,券有总量限制,领完即止,领取后 7 天内有效。
整理成结构化信息后喂给 AI,它输出了 24 条用例。我挑几条有代表性的贴出来:
| 编号 | 用例标题 | 前置条件 | 操作步骤 | 预期结果 | 优先级 |
|---|---|---|---|---|---|
| TC01 | 正常领取优惠券 | 用户未领过,券有库存 | 点击领取按钮 | 领取成功,券进入账户 | P0 |
| TC02 | 重复领取 | 用户已领过 | 再次点击领取 | 提示"每人限领一张" | P0 |
| TC03 | 库存为0时领取 | 券库存为0 | 点击领取 | 提示"已领完" | P1 |
| TC04 | 库存剩1时领取 | 库存为1 | 点击领取 | 领取成功,库存变0 | P1 |
| TC05 | 未登录领取 | 用户未登录 | 点击领取 | 跳转登录页 | P1 |
| TC06 | 领取后有效期计算 | 领取成功 | 查看券到期时间 | 为领取时间+7天 | P1 |
| TC07 | 并发领取最后一张 | 库存为1,两用户同时点 | 同时点击 | 仅一人成功 | P2 |
TC07 这条就是 AI 主动提出的并发场景,说实话我自己第一遍想用例时没考虑到。这就是 AI 发散的价值。
4.2 第二步:挑选可自动化的用例
24 条用例不是每条都值得自动化。我的筛选标准是:高频回归 + 结果可客观判定 + 执行成本低。像 TC01、TC02、TC03、TC05 这种,每次发版都要验,且结果非黑即白,必须自动化。TC06 涉及时间计算,可以自动化但要 mock 时间。TC07 并发场景自动化成本高,我选择手工验证。
最终我挑了 12 条做自动化,剩下的手工跑。这个比例比较健康,别追求 100% 自动化,投入产出比不划算。
4.3 第三步:用 Playwright 写脚本
先装环境,Python 3.9 以上:
pip install playwright playwright install然后写第一个用例 TC01 的脚本。这里有个经验:页面元素定位优先用get_by_role和get_by_text,少用 CSS 选择器。因为 CSS 选择器一旦前端改样式就失效,而基于角色和文本的定位更稳定。
from playwright.sync_api import sync_playwright def test_claim_coupon_success(): with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page() # 登录(略,可用 storage_state 复用登录态) page.goto("https://example.com/activity") # 定位领取按钮 page.get_by_role("button", name="立即领取").click() # 断言成功提示 expect(page.get_by_text("领取成功")).to_be_visible() browser.close()TC02 重复领取的脚本,关键是在领取前先构造“已领取”状态。我一般用接口直接给账号发一张券,再走 UI 领取:
def test_claim_coupon_duplicate(): with sync_playwright() as p: browser = p.chromium.launch(headless=True) context = browser.new_context() page = context.new_page() # 通过接口预置已领取状态 context.request.post("https://example.com/api/coupon/grant", data={"userId": "test001", "couponId": "c001"}) page.goto("https://example.com/activity") page.get_by_role("button", name="立即领取").click() expect(page.get_by_text("每人限领一张")).to_be_visible() browser.close()4.4 第四步:参数化与数据驱动
12 条用例如果每条都写一个函数,维护起来累。我把同类用例做了参数化。比如“异常输入”这一类,用pytest.mark.parametrize把数据抽出来:
import pytest @pytest.mark.parametrize("phone,password,expected", [ ("", "123456", "请输入手机号"), ("1380013800", "123456", "手机号格式不正确"), ("13800138000", "", "请输入密码"), ("13800138000", "12345", "密码长度不足"), ]) def test_login_invalid(page, phone, password, expected): page.goto("https://example.com/login") page.get_by_label("手机号").fill(phone) page.get_by_label("密码").fill(password) page.get_by_role("button", name="登录").click() expect(page.get_by_text(expected)).to_be_visible()这样加一条异常用例只需在参数列表里加一行,非常省事。
4.5 实操心得:脚本稳定性的三个技巧
技巧一:登录态复用。每条用例都走一遍登录太慢。用 Playwright 的storage_state把登录后的 cookie 存下来,后续用例直接加载:
context = browser.new_context(storage_state="auth.json")技巧二:断言用expect而不是assert。Playwright 的expect自带重试等待,元素晚几秒出现也能等到。用原生assert就得自己写等待逻辑,容易 flaky。
技巧三:失败自动截图。在 conftest 里加个钩子,用例失败时自动截图存证,排查问题时省去复现步骤:
@pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): outcome = yield report = outcome.get_result() if report.when == "call" and report.failed: page = item.funcargs.get("page") if page: page.screenshot(path=f"failures/{item.name}.png")5. 发现隐藏 Bug:AI 用例真正发挥价值的地方
5.1 一个真实的隐藏 Bug 案例
跑完 12 条自动化用例,11 条通过,1 条失败——TC04“库存剩1时领取”。预期是领取成功且库存变 0,实际结果是领取成功但库存变成了 -1。
这个 Bug 人工测试时大概率发现不了,因为正常测试都是拿有充足库存的券去测,谁会专门把库存调到 1 再领?但 AI 生成的边界用例恰好覆盖了这个点。定位下来是后端扣减库存的 SQL 写成了update stock = stock - 1,没有加where stock > 0的条件,并发或临界情况下就会扣成负数。
这就是 AI 用例的核心价值:它不知疲倦地覆盖那些人类觉得“没必要测”的边界。
5.2 另一个案例:时序问题
AI 还生成了一条“领取后立即使用”的用例。测试时发现,用户领取优惠券后马上点“去使用”,偶尔会提示“券不存在”。原因是领取接口写库和查询接口读库之间存在主从延迟,前端拿到领取成功的响应后立刻跳转,读到了从库的旧数据。
这个问题在人工测试时很难稳定复现,因为人手操作有延迟。但自动化脚本执行快,反而稳定触发了。后来通过“领取成功后前端延迟 500ms 再跳转”缓解了。
5.3 怎么区分“真 Bug”和“用例写错”
自动化用例失败时,先别急着提 Bug。我的排查顺序是:
- 看截图和日志:确认失败时的页面状态和接口返回。
- 手工复现:如果手工操作结果和脚本一致,大概率是真 Bug。
- 检查用例本身:预期结果是不是写错了?前置条件是不是没构造对?
- 检查环境:测试数据是不是被其他用例污染了?
我踩过的坑:有次一条用例一直失败,查了半天发现是前一条用例没清理数据,导致这条用例的前置条件不满足。后来我在每条用例的 setup 里加了数据清理,问题就没了。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 |
|---|---|---|
| 元素定位不到 | 页面未加载完 / 选择器失效 | 用 expect 等待,改用 role 定位 |
| 用例间歇性失败 | 数据污染 / 时序问题 | 加数据清理,检查异步逻辑 |
| 断言一直不通过 | 预期结果写错 | 手工复现核对预期 |
| 脚本执行超时 | 网络慢 / 死循环 | 加超时配置,检查页面跳转 |
| 登录态失效 | cookie 过期 | 重新生成 storage_state |
6. 把 AI 用顺手的几个经验
6.1 多轮对话比一次性提问效果好
别指望一次提问就拿到完美结果。我的习惯是分三轮:第一轮让它生成基础用例;第二轮我指出遗漏的维度(比如“你没覆盖并发场景,补充一下”);第三轮让它合并重复项、调整优先级。多轮打磨出来的用例,质量比一次生成高不少。
6.2 让 AI 帮你写测试数据
用例写好了,测试数据构造也是个麻烦事。我经常让 AI 生成边界数据,比如“生成 10 组手机号,包含合法、非法、边界长度”,它给的数据直接能填进参数化列表。这比手工编数据快多了。
6.3 用 AI 解释报错
脚本跑失败时,把报错信息贴给 AI,让它解释原因并给修复建议。它对 Playwright 的常见报错(比如 strict mode violation、timeout)解释得挺准,能省不少查文档的时间。但记住,它给的修复方案要自己验证,别直接信。
6.4 别让 AI 碰核心断言逻辑
我见过有人让 AI 直接生成整个测试脚本,包括断言。结果 AI 把断言写成了“检查页面包含‘成功’二字”,可页面上到处都有“成功”这个词,断言形同虚设。断言必须自己写,且要精确到具体的元素和文案。AI 可以帮你写操作步骤,但判断对错这件事,得人来把关。
6.5 建立自己的用例库
每次用 AI 生成的用例,我都会把有价值的沉淀到一个 Markdown 库里,按功能模块分类。下次遇到类似功能,直接参考历史用例,再让 AI 在此基础上补充,效率更高。这个库慢慢就成了团队资产。
7. 关于这套方法的边界
说句实在话,AI 生成测试用例不是万能的。它对业务逻辑复杂、状态流转多的场景帮助最大,因为这类场景人容易漏。但对纯 UI 样式、交互体验这类问题,AI 基本无能为力,还得靠人眼和设计稿比对。另外,AI 对性能、安全这类专项测试的用例生成能力也有限,它更擅长功能层面的发散。
还有一个现实问题:AI 生成的用例需要人工复核,如果需求文档本身写得含糊,AI 会基于猜测生成用例,复核成本反而高。所以这套方法的前提是需求信息足够清晰。需求越明确,AI 的输出越靠谱。
我在实际项目里用这套流程跑了三个模块,平均每个模块发现 2-3 个隐藏 Bug,其中至少一个是边界或时序相关的、人工测试容易漏的。投入的时间主要是前期整理需求和写脚本,一旦脚本跑起来,后续回归几乎零成本。这个投入产出比,我觉得值。
最后分享一个小技巧:如果你用的是支持长上下文的大模型,可以把整个模块的接口文档一次性喂进去,让它跨接口生成集成测试用例。我试过一次喂了 8 个接口的定义,它生成了一条“下单时优惠券已过期但订单仍按优惠价结算”的用例,直接命中了一个真实 Bug。这种跨模块的关联场景,正是 AI 比人强的地方。