目录[-]
一、背景
团队在日常测试工作中,面临几个痛点:
-
需求文档转用例效率低:产品需求写出来,测试人员需要手动一条条设计用例,耗时长、易遗漏
-
新人上手慢:新同事不熟悉业务,设计用例时经常漏掉边界场景和异常场景
-
标准不统一:不同人设计的用例风格差异大,质量参差不齐
我的思路是:能不能用AI,基于结构化的产品需求,自动生成测试用例?
核心目标:
-
测试左移:在需求阶段就介入,AI辅助生成用例基线
-
结构化输入:用YAML描述产品需求,让LLM精确理解
-
可复用:生成的用例直接导出Excel,团队可直接使用
二、整体架构
产品需求(YAML) → 向量化(Embedding) → 存入FAISS
↓
用户提问 → 检索相关字段定义 → LLM生成测试用例 → Excel输出
| 模块 | 技术栈 | 职责 |
|---|---|---|
| 需求结构化 | YAML | 描述字段、规则、校验条件 |
| 向量化 | OllamaEmbeddings (nomic-embed-text) | 将YAML文本块转为向量 |
| 向量存储 | FAISS | 本地向量检索,支持相似度匹配 |
| 用例生成 | Ollama (qwen2.5:7b) | 基于字段定义生成测试用例 |
| 输出 | pandas + openpyxl | 生成格式化的Excel用例文件 |
三、核心设计
1. YAML结构化需求(测试左移的关键)
传统需求文档是自然语言,LLM容易产生幻觉。我用YAML把需求“结构化”,让AI精确理解。
product:
name: AI智能翻译助手
version: 1.0
modules:
- name: 登录页面UI
fields:
- name: 手机号输入框
type: input
required: true
min_length: 11
max_length: 11
allowed: numeric
error_message:
length_error: "请输入11位手机号码"
required_error: "手机号不能为空"
ai_test_case_rules:
- 规则1:required: true → 必须生成【为空校验】用例
- 规则2:min_length、max_length存在 → 必须生成【边界值用例】
- 规则3:allowed: numeric → 必须生成【非数字校验用例】
核心思想:把“测试规则”写进YAML,AI按规则生成,不是瞎编。
2. 字典转文本块(用于向量检索)
YAML转成字典后,需要摊平成文本块,才能做向量检索:
def dict_to_texts(obj, path="", texts=None):
"""保留字典结构信息,转换为文本块"""
if isinstance(obj, dict):
for k, v in obj.items():
new_path = f"{path}.{k}" if path else k
dict_to_texts(v, new_path, texts)
elif isinstance(obj, list):
for i, item in enumerate(obj):
dict_to_texts(item, f"{path}[{i}]", texts)
else:
texts.append(f"{path}: {obj}")
return texts
生成的文本块示例:
product.name: AI智能翻译助手
modules[0].fields[0].name: 源语言
modules[0].fields[0].type: dropdown
modules[0].fields[0].required: True
3. FAISS向量库(带MD5缓存)
class VectorStoreManager:
def get_vectorstore(self, chunks=None):
# 检查MD5,判断文档是否变化
if self._check_changed():
# 文档变化 → 重建向量库
vectorstore = FAISS.from_embeddings(
text_embeddings=list(zip(texts, all_embeddings)),
embedding=self.embeddings
)
self._save_md5()
else:
# 文档未变 → 直接加载
vectorstore = FAISS.load_local("./faiss_db", self.embeddings)
return vectorstore
设计要点:
-
用MD5检测YAML文件变化,避免重复建库
-
本地持久化,重启不丢失
4. Prompt设计(规则驱动)
prompt = f"""你是资深测试工程师。根据以下字段定义生成测试用例。
【字段定义(JSON格式)】
{fields_json}
【生成规则】
1. 如果存在ai_test_case_rules规则,严格按照当前YAML里的规则生成测试用例
【输出格式】严格JSON数组
[
{{
"title": "用例标题",
"precondition": "前置条件",
"steps": ["步骤1", "步骤2"],
"expected": ["预期结果"]
}}
]
"""
关键:把测试规则写在YAML里,AI按规则执行,不是自由发挥。
5. 流式输出 + Excel格式化
# 流式输出,实时看到生成过程
for chunk in llm.stream(prompt):
print(chunk, end="", flush=True)
answer += chunk
# 解析JSON,导出Excel
cases = extract_json(answer)
df = pd.DataFrame(cases)
# 设置列宽、自动换行
worksheet.column_dimensions['G'].width = 60 # 测试步骤列加宽
四、运行效果
以“手机号输入框”字段为例,YAML定义了:
-
required: true
-
min_length: 11, max_length: 11
-
allowed: numeric
AI自动生成的用例:
| 标题 | 测试步骤 | 预期结果 |
|---|---|---|
| 手机号为空校验 | 1. 不输入手机号 2. 点击提交 |
提示"手机号不能为空" |
| 手机号长度边界值 | 1. 输入11位数字 2. 点击提交 |
校验通过 |
| 手机号长度小于11位 | 1. 输入10位数字 2. 点击提交 |
提示"请输入11位手机号码" |
| 手机号长度大于11位 | 1. 输入12位数字 2. 点击提交 |
提示"请输入11位手机号码" |
| 手机号包含非数字字符 | 1. 输入"138-0000-1111" 2. 点击提交 |
提示格式错误 |
覆盖了:正常值、边界值(等于/小于/大于)、异常值(非数字)
五、踩坑记录
坑1:LLM输出JSON格式不稳定
有时输出带markdown代码块,有时直接输出JSON,有时夹杂解释文字。
解决:用正则提取[...]部分,兼容多种输出格式。
坑2:FAISS序列化警告
FAISS加载时会提示allow_dangerous_deserialization。
解决:添加allow_dangerous_deserialization=True参数。
坑3:向量化慢
50个文本块,每个1-3秒,总耗时2-3分钟。
解决:用MD5缓存,只有YAML变化时才重建;用tqdm显示进度。
坑4:LLM生成的步骤是数组,Excel显示不友好
JSON中是["步骤1", "步骤2"],直接写入Excel是一行。
解决:转为带编号的换行文本1. 步骤1\n2. 步骤2。
六、团队价值
这套工具在团队中的定位:
| 角色 | 如何使用 | 价值 |
|---|---|---|
| 新人/初级测试 | AI生成的用例作为学习材料和工作指南 | 快速上手,保证基础覆盖 |
| 资深测试 | 专注集成测试、数据库校验、多模块交互 | 从重复劳动中解放,做高价值工作 |
| 团队整体 | 统一用例风格,建立基线 | 质量左移,缺陷前置发现 |
结论:这个工具的价值不只是“生成用例”,更是团队能力建设的工具——让新人快速上手,让老人发挥更大价值。
七、优化方向
| 方向 | 说明 |
|---|---|
| 支持更多字段类型 | 当前支持input/textarea/dropdown,扩展date/checkbox等 |
| 用例去重 | 多个字段可能生成重复用例,需自动去重 |
| 用例优先级 | 根据字段重要性自动标注P0/P1/P2 |
| 集成到CI | 需求变更时自动触发用例更新 |
八、总结
这套框架的核心思想是测试左移:
-
结构化输入:用YAML描述产品需求,AI精确理解
-
规则驱动:把测试规则写在YAML里,AI按规则执行
-
可量化:生成用例直接导出Excel,团队可用
-
可复用:FAISS向量库+MD5缓存,支持需求迭代
你不是在“写用例”,你是在“定义生成用例的规则”。
生成结果显示