【AI测试系统】第3篇:AI生成的测试用例太“水”?14年老兵:规则引擎+AI才是王炸组合

AI测试系统系列会写28篇,这是第3篇,会持续更新

AI生成的测试用例太"水"?14年老兵:规则引擎+AI才是王炸组合

第 2 篇的规则引擎 10 毫秒出 36 条用例,但说实话,步骤描述都是"进入功能页面 → 执行正常操作流程 → 提交操作"这种万能模板。测试工程师拿到手还得手动细化,否则根本没法执行。

说实话,第一次看到 AI 生成的用例我差点把咖啡喷出来------这哪是测试用例,这是废话模板。

AI Skill 要解决的就是这个问题。把需求丢给大模型,让它像真正的测试工程师一样思考:这个功能有哪些正常场景?哪些异常场景?边界值在哪里?数据怎么构造?

为什么不直接用 AI 生成所有用例?

很多人第一反应是:规则引擎出的用例这么水,直接用 AI 不就行了?

实际上不行,原因有三个:

  1. 成本:一个需求文档拆成 20-50 个功能点,每个都调 AI,一次 0.05-0.1 元,就是 1-5 元。企业级项目上千个需求,成本不小。
  2. 速度:AI 调用 5-30 秒,规则引擎 10 毫秒。用户点击"生成"后等 30 秒 vs 等 0.01 秒,体验天差地别。
  3. 确定性覆盖:CRUD、权限、边界值这些标准场景,规则引擎保证 100% 覆盖。AI 可能漏掉,因为它"随机"。

所以规则引擎的定位是保底 ------快、免费、稳定、覆盖标准场景。AI 的定位是锦上添花------创意、深入、覆盖边缘场景。两者配合,效率和成本都有保障。

但 AI 不是丢句话就完事。Prompt 设计得好,生成的用例可以直接用;设计得烂,生成的东西连看都不想多看一眼。经过多轮调优------特别是加了历史参考用例做风格对齐后,生成质量明显提升,分享几个关键经验。


一、Prompt 设计:五个要素缺一不可

rust 复制代码
def _build_prompt(self, requirement: str, context: Optional[Dict] = None) -> str:
    base_prompt = f"""
你是一个专业的测试工程师。请根据以下需求生成测试用例。

## 需求描述
{requirement}

## 历史参考用例
{json.dumps(context, ensure_ascii=False, indent=2) if context else "无"}

## 输出格式要求
请按以下 JSON 格式输出:
{{
    "test_cases": [
        {{
            "title": "用例标题",
            "description": "用例描述",
            "priority": "P0/P1/P2/P3",
            "type": "functional/api/performance/security",
            "preconditions": ["前置条件 1", "前置条件 2"],
            "steps": [
                {{
                    "step": 1,
                    "action": "操作步骤",
                    "expected": "预期结果"
                }}
            ],
            "test_data": "测试数据",
            "automation": true/false
        }}
    ]
}}

## 生成要求
1. 覆盖正常场景和异常场景
2. 包含边界条件测试
3. 优先级合理分配(P0 核心功能,P1 重要功能,P2 一般功能,P3 边缘功能)
4. 步骤清晰可执行
"""
    return base_prompt

这个 Prompt 包含了五个要素:

角色定义:"你是一个专业的测试工程师"。别小看这句话,加了和没加,生成的用例质量差很多。AI 会代入角色身份来思考问题。

需求描述:用户输入的需求文本。这是 AI 生成的素材来源。

历史参考用例:如果传入了 context(比如 RAG 检索到的历史用例),就放在这里。这招很关键------AI 看到历史用例的风格和质量后,新生成的用例会保持一致的水平。

输出格式要求:用 JSON Schema 指定输出结构。这比自然语言描述精确得多,AI 会严格按照这个结构生成内容。

生成要求:四条具体要求。没有这四条,AI 生成的用例往往只覆盖正常场景,漏掉异常和边界。


二、temperature 调参:0.7 是甜点

根据经验,temperature 不同值的效果大致如下:

temperature 效果 问题
0.3 用例很稳定,每次生成几乎一样 缺乏多样性,覆盖的场景太少
0.7 稳定性和多样性平衡 偶尔有低质量用例,需要评分过滤
1.0 创意十足,能想到很多边缘场景 质量不稳定,有时生成离谱的用例

最终选了 0.7。稳定性够用,多样性也够,偶尔的低质量用例用质量评分过滤掉就行。


三、质量评分:四维度打分,低于 0.7 的直接过滤

AI 生成的用例不能直接用,得先过质量评分这一关。我们的评分模型四个维度:

csharp 复制代码
def _calculate_quality_score(self, test_case: Dict) -> float:
    score = 0.0
    
    # 完整性(30%):步骤数 >= 3
    steps = test_case.get("steps", [])
    if len(steps) >= 3:
        score += 0.3
    
    # 清晰度(30%):标题 > 5 字符且描述 > 10 字符
    title = test_case.get("title", "")
    description = test_case.get("description", "")
    if len(title) > 5 and len(description) > 10:
        score += 0.3
    
    # 优先级(20%):P0=0.2, P1=0.15, P2=0.1, P3=0.05
    priority = test_case.get("priority", "P3")
    score += {"P0": 0.2, "P1": 0.15, "P2": 0.1, "P3": 0.05}.get(priority, 0.05)
    
    # 可自动化(20%)
    if test_case.get("automation", False):
        score += 0.2
    
    return min(score, 1.0)

权重分配的逻辑:步骤不全的用例没法执行(完整性最重要),描述不清楚的用例难以理解(清晰度次之),优先级和可自动化是加分项。

实际跑下来,质量分低于 0.7 的用例确实问题比较多------要么步骤太少,要么标题就写了两个字。过滤掉之后,剩下的用例基本可以直接用。


四、容错处理:LLM 不按套路出牌怎么办

大模型不是每次都乖乖返回 JSON。有时候在 JSON 前面加一句"好的,以下是生成的测试用例:",有时候在 JSON 后面加一句"希望对你有帮助"。直接 json.loads() 会报错。

我们的处理方式是用正则提取 JSON:

python 复制代码
def _parse_llm_response(self, response: str) -> List[Dict]:
    try:
        data = json.loads(response)
        return data.get("test_cases", [])
    except json.JSONDecodeError:
        # 从文本中提取 JSON
        import re
        json_match = re.search(r'{.*}', response, re.DOTALL)
        if json_match:
            try:
                data = json.loads(json_match.group())
                return data.get("test_cases", [])
            except:
                pass
        return []

re.DOTALL 标志让 . 匹配换行符,这样多行 JSON 也能正确提取。

注意 :这个正则会匹配从第一个 { 到最后一个 } 的内容。对大多数情况够用,但如果 LLM 返回了多个 JSON 块(比如先返回错误格式再返回正确格式),可能会匹配到无效内容。更精细的场景可以用 json.loads() 逐行尝试。


五、降级方案:API 挂了不能等死

python 复制代码
def _call_llm(self, prompt: str, requirement: str = "") -> str:
    api_key = os.getenv("DASHSCOPE_API_KEY", "")
    
    if not api_key:
        return self._get_rule_engine_response(requirement)  # 没 Key,降级到规则引擎
    
    try:
        response = httpx.post(
            "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation",
            headers={"Authorization": f"Bearer {api_key}", ...},
            json={"model": "qwen3.5-plus", "input": {...}, "parameters": {"temperature": 0.7, "max_tokens": 2000}},
            timeout=30.0
        )
        
        if response.status_code == 200:
            return response.json().get("output", {}).get("text", "")
        else:
            return self._get_rule_engine_response(requirement)  # API 报错,降级到规则引擎
    except Exception:
        return self._get_rule_engine_response(requirement)  # 网络异常,降级到规则引擎

def _get_rule_engine_response(self, requirement: str) -> str:
    """降级方案:使用规则引擎生成用例"""
    engine = self.rule_engine
    if engine and requirement:
        cases = engine.generate_cases_from_requirement(requirement)
        if cases:
            return json.dumps({"test_cases": cases}, ensure_ascii=False)
    
    # 规则引擎也不可用时,最后兜底用 Mock
    return self._get_mock_response()

三级降级:LLM → 规则引擎 → Mock 兜底

  • 第一级:正常调用 LLM,生成高质量用例
  • 第二级:API 挂了/没配置 Key,调用规则引擎生成针对性用例(实测 22 条,平均质量分 0.77,快、免费、真实)
  • 第三级:规则引擎也不可用(极端情况),返回硬编码 Mock 数据保证流程不崩

Mock 响应是两条登录用例的硬编码数据,只在最极端的情况下使用。正常情况下,规则引擎就能提供可用的用例。


六、成本估算:一次生成花多少钱

qwen3.5-plus 的定价大约是输入 0.02 元/千 tokens,输出 0.02 元/千 tokens。

一次用例生成大约消耗:

  • 输入:Prompt(约 800 tokens)+ 需求文本(约 200-500 tokens)= 1000-1300 tokens
  • 输出:5-10 条用例(约 1500-2500 tokens)

单次调用成本大约 0.05-0.1 元(具体取决于实际 token 消耗和模型定价)。

一次需求生成 10 条用例,成本约 0.05-0.1 元。比想象中便宜很多。


七、踩过的坑

坑 1:temperature 设太高。一开始设了 1.0,AI 生成的用例创意十足但质量不稳定。有时候一条用例的步骤写得像小说,测试工程师根本没法执行。降到 0.7 之后稳定多了。

坑 2:没有质量评分。第一版直接返回 AI 生成的所有用例,结果 30% 的用例质量很差------步骤只有 1-2 步,标题就两个字"登录"。加了质量评分过滤之后,低质量用例直接被过滤掉。

坑 3:API 超时没处理。有一次 DashScope 服务抖动,API 调用卡了 2 分钟没返回,前端一直转圈。加了 30 秒超时 + 降级方案之后,超时直接返回 Mock,用户感知不到。


八、规则引擎 + AI:怎么结合才不"水"

光知道分工不够,关键是怎么把两者串起来。我们的架构是这样的:

markdown 复制代码
需求文档
    │
    ├──▶ 规则引擎(10 毫秒)
    │       ├── CRUD 标准场景(增删改查各 1 条)
    │       ├── 权限检查(管理员 vs 普通用户)
    │       ├── 边界值测试(0、负数、超长、空值)
    │       └── 必填字段验证
    │       产出:15-25 条基础用例(覆盖标准场景)
    │
    ├──▶ AI Skill(5-30 秒)
    │       ├── 复杂业务逻辑组合(需要理解需求)
    │       ├── 边缘场景发现(需要创意和经验)
    │       ├── 安全测试场景(注入、越权、数据泄露)
    │       └── 性能测试思路(并发、大数据量)
    │       产出:5-10 条深度用例(覆盖边缘场景)
    │
    └──▶ 去重合并 + 质量评分
            ├── 标题相似度 > 80% 的合并
            ├── 质量分 < 0.7 的过滤
            └── 最终产出:18-30 条高质量用例

规则引擎怎么写才能不"水"?

核心就一条:从需求文本中提取实体和动作,生成针对性用例,而不是套万能模板。

markdown 复制代码
❌ 水的写法:
"进入功能页面 → 执行正常操作流程 → 提交操作"

✅ 不水的写法(从需求中提取):
需求:"用户可以修改商品名称和价格"

提取结果:
  - 实体:商品
  - 字段:名称、价格
  - 动作:修改

生成用例:
  用例 1:修改商品名称为有效值 → 验证名称更新成功
  用例 2:修改价格为 0 → 验证系统拒绝
  用例 3:修改价格为负数 → 验证系统拒绝
  用例 4:修改名称为空字符串 → 验证系统拒绝
  用例 5:同时修改名称和价格 → 验证两者都更新成功

标准测试模式套用:

模式 说明 适用场景
CRUD 增删改查全覆盖 所有有数据操作的模块
权限 不同角色的操作权限 有角色区分的系统
边界值 数字字段的边界 价格、数量、年龄等数值字段
等价类 有效/无效输入分类 所有输入框
异常流 网络异常、数据不存在 所有接口调用

把这些模式写进规则引擎,生成的用例就不再是"万能模板",而是有针对性的测试场景。


九、总结一下

AI 生成测试用例不是"丢句话让大模型写"那么简单。从 Prompt 设计、temperature 调参、质量评分、容错处理到降级方案,每个环节都影响最终质量。

我们的经验是:规则引擎出基础用例(快、便宜、结构统一)→ AI Skill 做精细打磨(具体、深入、覆盖边缘场景)→ 测试工程师最终评审(结合业务经验补充)。三层配合,效率和质量都有保障。

我的体会是:AI 不是替代测试工程师,是放大测试工程师的价值。 规则引擎解决"有没有",AI 解决"好不好",最终还得靠测试工程师的经验决定"用不用"。


你们用 AI 生成测试用例踩过什么坑?或者有什么更好的 Prompt 技巧?评论区聊聊,我整理一期读者实战经验合集。


下篇预告:第 4 篇讲 Skill引擎本身的架构------Markdown 声明 + Python 执行器,怎么做到写个 MD 文件就能扩展新功能。

相关推荐
fzil0011 小时前
自动投递简历 + 面试进度跟踪
人工智能·面试·职场和发展
Raink老师1 小时前
【AI面试临阵磨枪-34】单 Agent 与多 Agent(Multi-Agent)架构区别、适用场景、挑战
人工智能·ai 面试
LeesonWong1 小时前
从 PDF 到 MCP:让 AI Agent 按需查询你的简历
人工智能
灵机一物1 小时前
灵机一物AI原生电商小程序、PC端(已上线)-【AI 技术周报】2026 年 4 月第 4 周|模型、算力、商业化、安全全景梳理
人工智能
redreamSo1 小时前
一个只有70行的文件,凭什么拿下GitHub 10万星?
人工智能·开源
互联网志1 小时前
政策赋能校产融合 推动高校科技成果落地生根
大数据·人工智能·物联网
qcx232 小时前
Warp源码深度解析(四):AI Agent原生集成——MCP协议、代码索引与Skills系统
人工智能·ai·agent·源码解析·wrap
秦ぅ时2 小时前
保姆级教程|OpenAI tts-1-hd模型调用全流程(Python+curl+懒人用法)
开发语言·python
Muyuan19982 小时前
25.Paper RAG Agent 优化记录:上传反馈、计算器安全与 Chunk 参数调整
python·安全·django·sqlite·fastapi