【AI Agent 软件测试实战(08):AI Agent 自动生成测试用例:从需求分析到测试执行】
TL;DR
系列方向: 从零搭建 AI Test Agent,逐步实现大模型接入、工具调用、任务规划、异常恢复、HTTP 接口接入和自动化测试执行。
本篇重点: 在第 07 篇接入 HTTP 测试环境的基础上,让 Agent 根据业务需求自动提取测试点、生成结构化测试用例,并通过已有接口工具执行测试。
一、为什么要让 AI Agent 自动生成测试用例?
在实际测试工作中,编写测试用例并不是简单地列出几个输入和预期结果。
拿订单系统举例,产品需求可能只有这样一段描述:
订单创建成功后,订单金额必须大于 0。
订单状态只能是 pending、paid、cancelled。
用户查询订单时,如果订单不存在,应返回 404。
订单查询接口需要返回订单编号、金额和状态。
测试人员看到这些需求后,需要进一步分析:
- 正常订单应该如何验证?
- 金额为 0 怎么处理?
- 负数金额是否允许?
- 订单状态不合法怎么办?
- 查询不存在的订单应该返回什么?
- 接口响应字段缺失怎么办?
这些工作通常需要测试人员先分析需求,再编写测试用例,最后手动或通过自动化脚本执行。
如果把这套工作交给 AI Agent,可以形成一个新的执行流程:
这里需要明确一点:
AI 负责分析和生成测试用例,Python 负责校验和执行测试用例。
不能因为大模型生成了一段测试步骤,就直接允许它执行任意 Python 代码或者访问任意接口。
这也是本篇实现时需要坚持的设计原则。
二、本篇准备实现什么?
本篇继续使用第 07 篇的订单 API,不重新搭建 HTTP 服务。
已有接口:
GET /api/orders/{order_id}
已有工具:
query_order_detail
check_order_amount
check_order_status
本篇增加三个能力:
| 模块 | 作用 |
|---|---|
| 需求分析 | 从自然语言需求中提取业务规则 |
| 用例生成 | 生成结构化测试用例 |
| 用例执行 | 校验用例并调用 HTTP 工具执行 |
最终希望实现这样的效果:
输入:
请根据订单业务规则生成测试用例并执行。
1. 订单金额必须大于 0。
2. 订单状态只能是 pending、paid、cancelled。
3. 查询不存在的订单时,应返回 HTTP 404。
Agent 自动生成测试用例,然后调用 HTTP 接口执行。
三、先定义测试用例的数据结构
在真正调用大模型之前,先确定测试用例应该长什么样。
这是一个容易被忽略的细节。
如果不提前定义数据结构,大模型可能生成各种不同格式的结果:
测试场景:正常订单
测试数据:ORDER1001
预期结果:金额正确,状态正确
或者:
{
"name": "正常订单测试",
"order": "ORDER1001"
}
对于人来说,这些内容都能理解,但程序不一定能够稳定处理。
因此,我们先定义一个统一的 JSON 格式。
{
"case_id": "TC001",
"title": "正常订单查询",
"order_id": "ORDER1001",
"expected_http_status": 200,
"expected_business_valid": true,
"checks": [
"amount_positive",
"status_valid"
]
}
字段说明:
| 字段 | 含义 |
|---|---|
| case_id | 测试用例编号 |
| title | 测试用例名称 |
| order_id | 测试订单编号 |
| expected_http_status | 预期 HTTP 状态码 |
| expected_business_valid | 预期业务校验结果 |
| checks | 需要执行的检查项 |
这里没有让模型生成 Python 代码,也没有让模型直接生成 HTTP 请求。
它只负责生成结构化测试数据。
这样后续无论接入什么模型,执行层都可以保持稳定。
四、实现测试用例生成模块
新建 case_generator.py。
这个模块主要负责调用大模型,将自然语言需求转换成 JSON 测试用例。
import json
import os
from urllib.request import Request, urlopen
LLM_API_KEY = os.getenv("LLM_API_KEY")
LLM_BASE_URL = os.getenv("LLM_BASE_URL", "").rstrip("/")
LLM_MODEL = os.getenv("LLM_MODEL")
SYSTEM_PROMPT = """
你是一名资深软件测试工程师。
请根据用户提供的业务需求生成接口测试用例。
要求:
1. 只生成 JSON,不要输出 Markdown。
2. 不允许生成 Python 代码或可执行脚本。
3. 每个测试用例必须包含:
case_id、title、order_id、
expected_http_status、
expected_business_valid、checks。
4. expected_http_status 只能是合理的 HTTP 状态码。
5. checks 只能从以下范围选择:
amount_positive、status_valid。
6. 测试用例应覆盖正常场景和异常场景。
7. 不要编造实际接口已经执行的结果。
输出格式:
{
"cases": [
{
"case_id": "TC001",
"title": "正常订单查询",
"order_id": "ORDER1001",
"expected_http_status": 200,
"expected_business_valid": true,
"checks": [
"amount_positive",
"status_valid"
]
}
]
}
"""
def generate_cases(requirement):
if not LLM_API_KEY or not LLM_BASE_URL or not LLM_MODEL:
raise RuntimeError("请先配置模型服务环境变量")
payload = {
"model": LLM_MODEL,
"messages": [
{
"role": "system",
"content": SYSTEM_PROMPT
},
{
"role": "user",
"content": requirement
}
],
"temperature": 0
}
request = Request(
f"{LLM_BASE_URL}/chat/completions",
data=json.dumps(payload).encode("utf-8"),
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {LLM_API_KEY}"
},
method="POST"
)
with urlopen(request, timeout=60) as response:
result = json.loads(
response.read().decode("utf-8")
)
content = result["choices"][0]["message"]["content"]
return json.loads(content)
这里有一个需要注意的地方:
不同模型服务商对 JSON 输出模式的支持程度不一样。
如果模型经常在 JSON 前后添加解释文字,可以优先使用服务商支持的结构化输出或 JSON Mode。不要简单地通过字符串截取来假装完成了可靠解析。
另外,生成的 JSON 仍然是不可信输入,必须经过下一步校验。
五、增加测试用例校验模块
大模型生成的测试用例不一定正确。
例如,它可能生成:
{
"case_id": "TC001",
"title": "正常订单测试",
"order_id": "ORDER1001",
"expected_http_status": 999,
"expected_business_valid": true,
"checks": [
"execute_shell"
]
}
如果直接执行,就会产生问题。
因此,我们需要增加一个用例校验模块。
新建 case_validator.py:
ALLOWED_CHECKS = {
"amount_positive",
"status_valid"
}
ALLOWED_HTTP_STATUS = {
200,
400,
404,
500
}
def validate_cases(data):
if not isinstance(data, dict):
raise ValueError("用例数据必须是 JSON 对象")
cases = data.get("cases")
if not isinstance(cases, list) or not cases:
raise ValueError("cases 必须是非空数组")
case_ids = set()
for case in cases:
if not isinstance(case, dict):
raise ValueError("每条用例必须是对象")
required_fields = {
"case_id",
"title",
"order_id",
"expected_http_status",
"expected_business_valid",
"checks"
}
if not required_fields.issubset(case):
raise ValueError(
f"用例缺少必要字段: {case}"
)
case_id = case["case_id"]
if not isinstance(case_id, str) or not case_id.strip():
raise ValueError("case_id 必须是非空字符串")
if case_id in case_ids:
raise ValueError(f"用例编号重复: {case_id}")
case_ids.add(case_id)
if not isinstance(case["title"], str):
raise ValueError("title 必须是字符串")
if not isinstance(case["order_id"], str):
raise ValueError("order_id 必须是字符串")
if case["expected_http_status"] not in ALLOWED_HTTP_STATUS:
raise ValueError("不支持的预期 HTTP 状态码")
if not isinstance(case["expected_business_valid"], bool):
raise ValueError("expected_business_valid 必须是布尔值")
checks = case["checks"]
if not isinstance(checks, list):
raise ValueError("checks 必须是数组")
if not set(checks).issubset(ALLOWED_CHECKS):
raise ValueError("包含不允许的检查项")
return True
这一步的意义不只是检查 JSON 格式。
它还建立了 Agent 和执行程序之间的边界:
- 模型可以提出测试场景。
- 模型可以填写测试数据。
- 模型不能增加任意工具。
- 模型不能修改程序的执行权限。
实际工程中,还应增加字段长度限制、用例数量限制、订单编号格式校验和重复场景检查。
六、实现测试用例执行模块
接下来,让生成的测试用例真正执行起来。
这里直接复用第 07 篇的 query_order_detail()。
不过,本篇需要对 HTTP 查询结果进行更严格的处理。
新建 case_runner.py:
from http_tools import query_order_detail
ALLOWED_STATUSES = {
"pending",
"paid",
"cancelled"
}
def validate_order(order, checks):
results = []
for check in checks:
if check == "amount_positive":
amount = order.get("amount")
passed = (
isinstance(amount, (int, float))
and not isinstance(amount, bool)
and amount > 0
)
results.append({
"check": check,
"passed": passed,
"actual": amount,
"message": "订单金额必须大于 0"
})
elif check == "status_valid":
status = order.get("status")
passed = status in ALLOWED_STATUSES
results.append({
"check": check,
"passed": passed,
"actual": status,
"message": "订单状态必须属于允许范围"
})
return results
def run_case(case):
result = {
"case_id": case["case_id"],
"title": case["title"],
"passed": False,
"http_status": None,
"checks": [],
"error": None
}
response = query_order_detail(case["order_id"])
if not response["success"]:
result["http_status"] = response.get("http_status")
result["error"] = response.get("error")
# 请求失败时,只能根据实际 HTTP 结果判断预期是否满足。
result["passed"] = (
result["http_status"]
== case["expected_http_status"]
)
return result
actual_status = response["http_status"]
result["http_status"] = actual_status
if actual_status != case["expected_http_status"]:
result["error"] = (
f"HTTP 状态码不符合预期:"
f"实际 {actual_status},"
f"预期 {case['expected_http_status']}"
)
return result
order = response.get("data")
if not isinstance(order, dict):
result["error"] = "接口没有返回有效订单数据"
return result
checks = validate_order(
order,
case["checks"]
)
result["checks"] = checks
actual_business_valid = all(
item["passed"] for item in checks
)
result["passed"] = (
actual_business_valid
== case["expected_business_valid"]
)
return result
def run_cases(cases):
results = []
for case in cases:
results.append(run_case(case))
return results
这里做了一个重要调整:
测试用例是否通过,不能简单等同于业务断言是否通过。
例如,负数金额测试用例的预期就是发现异常数据。
如果接口返回了负数金额,并且程序正确识别出金额不合法,那么这个测试用例应该判定为通过。
因此这里使用:
result["passed"] = (
actual_business_valid
== case["expected_business_valid"]
)
也就是说:
| 实际业务校验 | 预期业务校验 | 测试用例结果 |
|---|---|---|
| 合法 | 合法 | 通过 |
| 不合法 | 不合法 | 通过 |
| 合法 | 不合法 | 失败 |
| 不合法 | 合法 | 失败 |
这才是测试用例真正需要表达的含义。
七、整合项目入口
现在将需求分析、用例生成、用例校验和执行整合起来。
修改 main.py:
import json
from case_generator import generate_cases
from case_validator import validate_cases
from case_runner import run_cases
def main():
requirement = """
订单业务规则:
1. 订单金额必须大于 0。
2. 订单状态只能是 pending、paid、cancelled。
3. 查询不存在的订单时,应返回 HTTP 404。
4. 正常订单查询接口应返回 HTTP 200。
"""
print("========== 1. 生成测试用例 ==========")
generated = generate_cases(requirement)
print(json.dumps(
generated,
ensure_ascii=False,
indent=2
))
print("\n========== 2. 校验测试用例 ==========")
validate_cases(generated)
print("测试用例校验通过")
print("\n========== 3. 执行测试用例 ==========")
results = run_cases(generated["cases"])
print(json.dumps(
results,
ensure_ascii=False,
indent=2
))
print("\n========== 4. 测试统计 ==========")
total = len(results)
passed = sum(
1 for item in results
if item["passed"]
)
failed = total - passed
print(f"总用例数:{total}")
print(f"通过数量:{passed}")
print(f"失败数量:{failed}")
if __name__ == "__main__":
main()
项目结构现在变成:
ai-test-agent/
├── mock_server.py
├── http_tools.py
├── agent.py
├── case_generator.py
├── case_validator.py
├── case_runner.py
├── main.py
└── requirements.txt
其中:
mock_server.py:提供本地订单 API。http_tools.py:封装 HTTP 请求。agent.py:保留前面文章的 Tool Calling Agent。case_generator.py:根据需求生成测试用例。case_validator.py:检查生成结果是否合法。case_runner.py:执行测试用例。main.py:串联完整流程。
本篇入口采用独立的用例执行器,便于把生成和执行过程拆开验证。下一步再将它接入第 05、06 篇的任务管理和状态持久化模块。
八、测试一下:生成的用例应该是什么样?
启动第 07 篇的 HTTP 服务:
python mock_server.py
然后配置模型服务:
export LLM_API_KEY="你的API_KEY"
export LLM_BASE_URL="你的模型服务地址/v1"
export LLM_MODEL="你的模型名称"
运行:
python main.py
模型可能生成类似下面的用例:
{
"cases": [
{
"case_id": "TC001",
"title": "正常订单查询",
"order_id": "ORDER1001",
"expected_http_status": 200,
"expected_business_valid": true,
"checks": [
"amount_positive",
"status_valid"
]
},
{
"case_id": "TC002",
"title": "负数金额订单校验",
"order_id": "ORDER1002",
"expected_http_status": 200,
"expected_business_valid": false,
"checks": [
"amount_positive",
"status_valid"
]
},
{
"case_id": "TC003",
"title": "不存在订单查询",
"order_id": "NOT_FOUND",
"expected_http_status": 404,
"expected_business_valid": false,
"checks": []
}
]
}
这只是符合当前测试数据和业务规则的预期示例,实际生成结果会受到模型理解和生成质量影响,不能保证每次都完全一致。
如果执行结果与预期一致,可能得到:
========== 4. 测试统计 ==========
总用例数:3
通过数量:3
失败数量:0
同样,这里是示例输出,不代表已经实际运行模型和接口得到的结果。
九、这套方案还有哪些不足?
到这里,已经实现了一个基础的 AI 测试用例生成和执行流程。
但如果真的放进企业测试项目,我认为还有几个问题必须解决。
1. AI 生成的用例不一定覆盖完整
模型可能只生成正常场景和少量异常场景,却遗漏边界值、组合条件和权限场景。
因此不能简单地把生成数量当成覆盖质量。
后续可以增加需求条款与测试用例之间的映射关系,检查每条需求是否都有对应测试用例。
2. 预期结果不能完全交给 AI 决定
这是自动生成测试用例时最需要警惕的问题。
如果业务需求写得不清楚,模型可能自行推测某个异常应该返回 400 还是 422。
这种情况下,即使生成的用例结构正确,也不代表预期结果正确。
更合理的方式是:
- 让 AI 提取业务规则。
- 对关键规则进行人工确认。
- 将确认后的规则保存为测试规范。
- 再根据规范生成和执行测试用例。
3. 当前执行器只支持少量检查项
本篇只有:
amount_positive
status_valid
真实项目中还需要支持:
- 必填字段检查。
- 字段类型检查。
- 字符串长度检查。
- 日期格式检查。
- 金额精度检查。
- 响应结构检查。
- 业务错误码检查。
这些规则应该由程序实现,而不是让模型临时生成判断代码。
4. 测试数据需要单独管理
本篇直接使用固定订单编号:
ORDER1001
ORDER1002
NOT_FOUND
实际项目中,测试数据可能需要动态创建、清理和隔离。
如果多个测试任务同时执行,还需要避免数据互相影响。
5. 还没有形成完整的测试追踪体系
现在已经能生成用例并执行,但还缺少:
- 每个需求对应哪些测试用例。
- 每个用例执行了几次。
- 每个用例调用了哪些接口。
- 失败时的请求和响应信息。
- 测试报告和历史执行记录。
这些内容会在下一篇继续完善。
十、本篇总结
本篇在第 07 篇 HTTP 接口接入的基础上,完成了 AI 自动生成测试用例的基本流程。
| 实现模块 | 完成内容 |
|---|---|
| 需求分析 | 将自然语言业务需求交给模型 |
| 用例生成 | 输出结构化 JSON 测试用例 |
| 用例校验 | 检查字段、编号、状态码和检查项 |
| 接口执行 | 复用 HTTP 查询工具 |
| 业务断言 | 检查订单金额和状态 |
| 结果统计 | 统计用例通过和失败数量 |
从工程角度看,这篇最重要的不是让 AI 生成了几条测试用例,而是把原本由测试人员手工完成的几个步骤连接起来:
需求输入 → 测试点分析 → 用例生成 → 用例校验 → 接口执行 → 测试结果。
当然,AI 生成的用例仍然需要经过校验,关键业务规则也不能完全依赖模型判断。
更多推荐


所有评论(0)