您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center 汽车系统工程   模型库  
会员   
   
知识图谱、本体论、RAG与大模型
8月22-23日 北京+线上
企业架构方法与实践
8月27-28日 深圳+线上
AI智能体开发技术实践
9月17-18日 厦门+线上
     
   
 订阅
造一个测试用例生成器:从需求文档到结构化用例(附完整代码)
 
作/译者:测试老赵
  9   次浏览      1 次
 2026-8-17
 
编辑推荐:
本文主要讲解了如何从需求文档到结构化用例造一个测试用例生成器相关内容。希望对你的学习有帮助。
本文来自于微信公众号测试老赵 ,由火龙果软件Alice编辑推荐。

上篇结尾说了,从这篇开始进入实战板块——把前面三篇讲的Agent骨架、Prompt链、API接入全部串起来,造能落地的工具。

第一个就是测试用例生成器。

为什么选这个打头?因为它是我造的第一个真正"能用"的AI测试工具,也是踩坑最多的一个。前面三篇里提到的所有坑——Prompt太笼统、步骤衔接没容错、数据格式不对齐——基本都是在造这个工具的过程中踩的。

但那些是"基础搭建"的坑。真正把它从一个"能跑的脚本"变成"团队愿意用的工具",又踩了一轮完全不同的坑。

第一版:技术上跑通了,实际上没人用

我的第一版用例生成器,就是S4-03里写的那个三步Agent:拆维度→写用例→格式化。技术上完全跑通,输入PRD文本,30秒输出一批结构化用例。

我特别兴奋地拿给同事用。她试了一次,说:"挺好的,但我还是自己写比较快。"

我当时不服气。跟着她看了一遍她怎么写用例的:她拿到PRD后,先花5分钟通读,在关键段落画线标记;然后打开团队的Excel模板,按模块填用例;写的过程中她会翻看历史用例,看看类似功能上次怎么测的;写完还会跟开发确认几个边界场景。

我的工具帮她省了什么?省了"把PRD读一遍然后想测试点"这个环节。但这只是她写用例流程的第一步。后面的查历史用例、跟开发确认、填团队模板——这些我的工具都没覆盖。

你的工具如果只解决了用户工作流中的一个环节,而剩下的环节还得手动做,那用户切换工具的成本可能比直接手动做还高。

这是第一版最大的问题:我只想着"用AI生成用例",没想过"生成用例之后呢?"

第二版:补上"最后一公里"

第二版做了三件事,每件都是被现实打脸之后的反应。

第一件:导入团队Excel模板。

第一版输出的用例格式是我自己定义的JSON。同事拿过去还得手动转成团队模板格式。这个"最后一公里"把工具的效率优势全吃了。

我找同事要了团队的Excel模板,分析了一下结构——用例编号、模块、场景描述、前置条件、操作步骤、预期结果、优先级、备注。然后写了一个导出模块,直接按模板格式输出Excel。

这一步做完之后,同事的态度从"挺好的但我不太用"变成了"偶尔用一下"。进步了,但还不够。

第二件:加历史用例参考。

同事写用例时会翻历史用例,看看类似功能上次怎么测的。我的工具完全没这个能力——它只看当前PRD,不知道这个功能以前怎么测的。

我加了一步:在拆完维度之后,让Agent去测试平台搜同一模块的历史用例,把相关的作为参考喂给"写用例"那一步的Prompt。

这一步效果出乎意料地好。AI生成的用例质量明显提升——因为有了历史用例做参考,它不再只基于PRD字面内容"猜"测试点,而是结合了团队过去的测试经验。生成的用例风格也更贴近团队习惯,同事说"看着像是我们组的人写的"。

但这一步也带来了新问题:历史用例有时候太多,全喂进去会超token限制。我的处理方式是让Agent先筛选——只取和当前功能相关的、近三个月的、最多20条。筛选逻辑本身又是一个Prompt,加了十行代码,但效果天差地别。不筛选的时候,200条历史用例一股脑塞进去,大模型注意力被稀释,生成的用例反而比不加参考还差。

加参考数据不是越多越好,是"筛过的好"。这个教训后来我用在所有需要喂上下文的工具上。

第三件:加"覆盖率自查"。

这是被同事一句话逼出来的。有一次我拿生成的用例给她看,她扫了一眼说:"你这个登录模块的用例,没覆盖到'记住登录态'。"

我回去查,PRD里确实提了"记住登录态",但AI在拆维度的时候把它归到"登录功能"下面,写用例的时候又只写了"登录成功"和"登录失败","记住登录态"被遗漏了。

这不是S4-03里讲的"大模型遗忘排在前面的测试点"——那次是注意力衰减。这次是理解偏差:它把"记住登录态"理解成了登录功能的一个子项,而不是一个独立的测试场景。

我在格式化输出之前加了一个"覆盖率自查"步骤:让Agent拿生成的用例列表,回头对照PRD原文,逐段检查"这段内容是否有对应的用例覆盖",没有就标注出来。

# 覆盖率自查:用例列表 vs PRD原文,找遗漏
coverage_gaps = call_llm(prompt_coverage_check, {
   "prd": prd_text,
   "test_cases": generated_cases
})
# 输出示例:["PRD第3段'记住登录态'无对应用例", "PRD第5段'第三方登录'无对应用例"]

 

这个自查步骤不是100%准确——它有时候会漏报,有时候会误报。但即使只有70%的准确率,它帮我发现的遗漏也比我自己逐条核对快得多。AI自查不需要完美,只需要比你手动检查快就够了。

第三版:让AI说出"我不知道的地方"

第二版跑了一段时间后,我发现一个规律:AI生成的用例大概能覆盖70%-80%的场景,剩下的20%-30%是它想不到的——要么是业务规则太隐蔽(比如"连续3次输错密码后锁定账号,但VIP用户锁定后可以自助解锁"),要么是跨模块联动(比如"登录失败后同时触发风控告警")。

这些场景AI想不到,因为它没有业务上下文。但它知道"这里可能有问题"——前提是你给它一个机会说出来。

我在工具里加了一个"人工补充"环节:AI生成完用例后,不是直接导出,而是先展示一个"不确定区域"列表——它标注自己在生成过程中"不确定"或"觉得可能遗漏"的地方。然后我花两分钟补充,再导出。

这个环节的关键不是补充了多少,而是让AI说出"我不确定"。一个诚实的"我不确定"比一个自信的"我覆盖了所有场景"有价值得多。

实际用下来,"不确定区域"平均每次列3-5条,其中大概2条是我确实需要补的。这2条往往是业务逻辑相关的——AI看到了PRD里的某个描述,但它不确定这个描述意味着什么测试场景,就标注出来问我。这比我自己逐段读PRD找遗漏高效得多。

完整链路长什么样

三版迭代之后,用例生成器的完整链路是这样的:

    1. 输入PRD文本 → 拆测试维度(人工确认)
    2. 搜历史用例 → 筛选相关的近20条作为参考
    3. 按维度+历史参考 → 写具体用例(超过8个测试点自动分批)
    4. 覆盖率自查 → 标注PRD中未被覆盖的段落
    5. 人工补充 → 我花2分钟补"不确定区域"
    6. 按团队模板 → 导出Excel
    7. 调API → 写入测试平台(确认后写入)

从第一版的"30秒生成一堆JSON",到第三版的"3分钟走完全链路,直接入库"。慢了两分半,但从"没人用"变成了"每周用三次以上"。

同事后来跟我说了句话,我觉得是对这个工具最好的评价:"以前你那个工具是帮你干活的,现在这个是帮我干活的。"区别在哪?第一版是从"我能做什么"出发设计的,第三版是从"她需要什么"出发设计的。

工具的价值不在于"多快生成",而在于"生成的东西能不能直接用"。

完整代码如下:

"""
测试用例生成器 - 完整实现
从需求文档(PRD)到结构化用例,完整链路
依赖安装:
   pip install openai openpyxl requests
使用方式:
    python test_case_generator.py --prd prd.txt --project "登录模块"
"""
import json
import time
import argparse
from pathlib import Path
from openai import OpenAI
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, PatternFill
import requests
# ============================================================
# 配置区
# ============================================================
# 大模型配置
LLM_API_KEY = "your-api-key-here"
LLM_BASE_URL = "https://api.openai.com/v1"
LLM_MODEL = "gpt-4o-mini"
# 测试平台API配置
PLATFORM_BASE_URL = "https://test-platform.company.com/api/v1"
PLATFORM_TOKEN = "your-platform-token-here"
# 批处理参数
BATCH_SIZE = 6 # 超过6个测试点自动分批,6是舒适区(S4-03经验)
HISTORY_CASE_LIMIT = 20 # 历史用例最多取20条
# 初始化大模型客户端
llm_client = OpenAI(api_key=LLM_API_KEY, base_url=LLM_BASE_URL)
# ============================================================
# 大模型调用封装
# ============================================================
def call_llm(prompt: str, input_data: str, temperature: float = 0.3) -> str:
   """
   调用大模型,返回纯文本响应。
   temperature默认0.3,测试场景需要稳定性而非创造性。
   Args:
      prompt: 系统提示词/指令
      input_data: 输入内容(PRD文本、上一步输出等)
      temperature: 温度参数
   Returns:
      大模型返回的文本
   """
    try:
      response = llm_client.chat.completions.create(
         model=LLM_MODEL,
         messages=[
            {"role": "system", "content": prompt},
            {"role": "user", "content": input_data}
         ],
         temperature=temperature,
         response_format={"type": "json_object"}       # 强制JSON输出
      )
       return response.choices[0].message.content
   except Exception as e:
      print(f"[ERROR] LLM调用失败: {e}")
       raise
def call_llm_json(prompt: str, input_data: str, temperature: float = 0.3) -> dict:
   """
   调用大模型并解析为JSON dict。
    自动处理大模型偶尔返回非标准JSON的情况。
   """
   raw = call_llm(prompt, input_data, temperature)
   try:
      return json.loads(raw)
   except json.JSONDecodeError:
      # 尝试提取JSON块(大模型有时会包裹markdown代码块)
      import re
      match = re.search(r'\{.*\}|\[.*\]', raw, re.DOTALL)
      if match:
         return json.loads(match.group())
      raise ValueError(f"无法解析LLM输出为JSON: {raw[:200]}")
# ============================================================
# 人工确认环节
# ============================================================
def confirm(label: str, content: str) -> bool:
   """
   人工确认环节。打印内容,等待用户确认。
   这是"暂停键"——在关键步骤留人工把关。
   Args:
      label: 步骤名称
      content: 待确认内容
   Returns:
      True=确认通过,False=需要重跑
   """
   print(f"\n{'='*60}")
   print(f"【人工确认】{label}")
   print(f"{'='*60}")
   print(content)
   print(f"{'='*60}")
   while True:
      choice = input("确认无误?(y=继续 / n=重跑此步 / q=退出): ").strip().lower()
      if choice == 'y':
         return True
      elif choice == 'n':
         return False
      elif choice == 'q':
         print("用户退出。")
      exit(0)
   else:
      print("请输入 y/n/q")
# ============================================================
# Prompt 模板(每一步的指令)
# ============================================================
# 第一步:拆测试维度
PROMPT_SPLIT_DIMENSIONS = """\
你是一位资深测试工程师。请分析以下需求文档,按以下五个维度拆分测试点:
1. 功能验证
2. 边界值
3. 异常场景
4. 性能关注点
5. 安全风险
每个维度列出3-5个测试点,测试点要具体、可测试。
输出格式(JSON):
{
   "dimensions": [
      {"维度": "功能验证", "测试点":       ["登录成功", "登录失败", "密码错误"]},
{"维度": "边界值", "测试点":    ["密码最短长度", "密码最长长度"]}
]
}
注意:
- 测试点要基于需求文档内容,不要凭空捏造
- 每个维度的测试点不超过5个
- 如果某个维度在需求中没有相关内容,返回空数组
"""
# 第二步:按维度+历史参考写用例
PROMPT_GENERATE_CASES = """\
你是一位资深测试工程师。请根据以下测试维度和历史用例参考,编写具体的测试用例。
输入数据格式(JSON):
{
   "dimensions": [...], // 测试维度和测试点
   "history_cases": [...] // 同模块历史用例参考(可能为空)
}
每条用例必须包含以下字段:
- 模块: 功能模块名称
- 场景描述: 一句话说清楚测什么场景
- 前置条件: 执行用例前需要满足的条件
- 操作步骤: 逐步描述操作(数组,每步一个字符串)
- 预期结果: 执行后应该出现的结果
- 优先级: P0/P1/P2
- 备注: 特殊说明(可为空字符串)
输出格式(JSON):
{
   "cases": [
      {
         "模块": "登录",
         "场景描述": "正确的账号密码登录成功",
         "前置条件": "账号已注册且状态正常",
         "操作步骤": ["打开登录页面", "输入正确账号",          "输入正确密码", "点击登录按钮"],
         "预期结果": "登录成功,跳转到首页",
         "优先级": "P0",
         "备注": ""
      }
   ]
}
要求:
- 参考历史用例的写法风格,但不要照搬
- 每个测试点至少生成一条用例
- 操作步骤要具体到可执行,不要写"执行相关操作"这种模糊描述
"""
# 覆盖率自查
PROMPT_COVERAGE_CHECK = """\
你是一位测试质量审查员。请对照需求文档原文,检查测试用例的覆盖率。
输入数据(JSON):
{
   "prd": "需求文档原文",
    "test_cases": [生成的测试用例列表]
}
请逐段检查需求文档,找出"有需求描述但没有对应测试用例"的地方。
输出格式(JSON):
{
   "gaps": [
   {
      "prd段落": "需求文档中的原文片段",
      "遗漏说明": "为什么没有覆盖到",
      "建议": "应该补充什么用例"
   }
   ],
"coverage_rate": "预估覆盖率(如85%)"
}
注意:
- 只报告确实遗漏的,不要把"已覆盖但不够细"的算作遗漏
- 如果所有段落都有覆盖,返回空gaps数组
"""
# 不确定区域标注
PROMPT_UNCERTAINTY = """\
你是一位测试工程师。在刚才生成测试用例的过程中,有些地方你可能不确定。
请回顾你生成的用例和需求文档,标注你"不确定"或"觉得可能遗漏"的地方。
不确定的典型情况:
- 需求文档描述模糊,你做了假设但不确定是否正确
- 涉及业务规则,你没有足够的上下文判断
- 跨模块联动,你不确定是否需要测试
- 边界值不明确,你按常规处理但可能有特殊情况
输出格式(JSON):
{
   "uncertainties": [
   {
       "区域": "不确定的具体内容",
      "原因": "为什么不确定",
      "建议操作": "建议人工确认什么"
    }
   ]
}
注意:
- 诚实标注,不确定就说不确定
- 不要为了显得全面而编造不确定项
- 如果确实没有不确定的地方,返回空数组
"""
# ============================================================
# 测试平台API封装(S4-05的封装层)
# ============================================================
class TestPlatformAPI:
   """
   测试平台API封装。
   所有HTTP请求细节集中在这里,主流程只调用方法。
   换平台/换接口/换鉴权方式,只改这里。
   """
   def __init__(self, base_url: str, token: str):
      self.base_url = base_url.rstrip("/")
      self.session = requests.Session()
      self.session.headers.update({
         "Authorization": f"Bearer {token}",
         "Content-Type": "application/json"
      })
def search_cases(self, module_name: str, limit: int = 20) -> list:
      """
      搜索同模块的历史用例。
      用于第二步的历史参考。
      """
      url = f"{self.base_url}/testcases/search"
      params = {
          "module": module_name,
         "limit": limit,
         "sort": "update_time:desc"  # 按更新时间倒序,取最新的
      }
       resp = self.session.get(url, params=params, timeout=10)
      resp.raise_for_status()
      data = resp.json()
      # 实际API返回可能包了一层壳,取data字段
      if isinstance(data, dict) and "data" in data:
         return data["data"]
      return data
   def create_case(self, case_data: dict) -> dict:
      """
      创建单条测试用例。
      返回平台返回的用例信息(含用例ID)。
      """
      url = f"{self.base_url}/testcases"
      resp = self.session.post(url, json=case_data, timeout=10)
      resp.raise_for_status()
       return resp.json()
   def batch_create_cases(self, cases: list) -> list:
      """
      批量创建测试用例。
      返回每条用例的创建结果(含用例ID和平台链接)。
      """
      results = []
      for i, case in enumerate(cases):
         try:
            result = self.create_case(case)
             results.append({
               "场景描述": case.get("场景描述", ""),
               "status": "success",
               ""case_id": result.get("data", {}).get("id", ""),
               ""url": result.get("data",             {}).get("url", "")
          })
          print(f" [{i+1}/{len(cases)}] 创建成功: {case.get('场景描述', '')}")
       except Exception as e:
         results.append({
               ""场景描述": case.get("场景描述", ""),
               ""status": "failed",
               ""error": str(e)
           })
            print(f" [{i+1}/{len(cases)}] 创建失败: {case.get('场景描述', '')} - {e}")
        # 避免请求太快被限流
         time.sleep(0.3)
     return results
# ============================================================
# 数据转换层(S4-05:大模型输出 → API格式)
# ============================================================
def transform_cases_for_platform(cases: list, project_name: str) -> list:
"""
   将大模型输出的用例格式,转换为测试平台API要求的格式。
   字段映射、结构重组、默认值填充都在这里。
   大模型输出格式:
       {"模块": "...", "场景描述": "...", "操作步骤": [...], "预期结果": "...", ...}
平台API格式:
      {"name": "...", "module":       "...", "test_steps": [{"action": "...", "expected": "..."}],
"expect_result": "...", "priority": "...", "project": "..."}
"""
transformed = []
for case in cases:
      # 字段映射
      platform_case = {
      "name": case.get("场景描述", ""),
      "module": case.get("模块", ""),
      "project": project_name,
       "priority": case.get("优先级", "P2"),
      "precondition": case.get("前置条件", ""),
      "expect_result": case.get("预期结果", ""),
   }
    # 操作步骤转换:从字符串数组转为对象数组
   steps = case.get("操作步骤", [])
    if isinstance(steps, list):
       platform_case["test_steps"] = [
          {"action": step, "expected": ""} for step in steps
      ]
    elif isinstance(steps, str):
       platform_case["test_steps"] = [{"action": steps, "expected": ""}]
    else:
       platform_case["test_steps"] = []
    # 必填字段校验
    if not platform_case["name"]:
       print(f" [WARN] 用例缺少场景描述,跳过: {case}")
       continue
    if not platform_case["test_steps"]:
       print(f" [WARN] 用例缺少操作步骤,跳过: {platform_case['name']}")
       continue
    transformed.append(platform_case)
  return transformed
def transform_history_for_llm(history_cases: list) -> list:
    """
   将平台返回的历史用例,转换为大模型参考用的精简格式。
    平台返回的字段很多,只取有用的,避免token浪费。
   """
    simplified = []
    for case in history_cases[:HISTORY_CASE_LIMIT]:
       simplified.append({
          "场景": case.get("name", ""),
         "步骤": [s.get("action", "") if isinstance(s, dict) else str(s)
             for s in case.get("test_steps", [])],
         "预期": case.get("expect_result", ""),
          "优先级": case.get("priority", "")
       })
    return simplified
# ============================================================
# Excel导出模块(第二版新增)
# ============================================================
def export_to_excel(cases: list, output_path: str, coverage_gaps: list = None):
   """
    按团队模板格式导出Excel。
   包含用例明细和覆盖率自查报告两个Sheet。
   """
    wb = Workbook()
    # --- Sheet1: 测试用例 ---
    ws = wb.active
    ws.title = "测试用例"
   # 表头样式
    header_font = Font(bold=True, size=11)
    header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
   header_font_white = Font(bold=True, size=11, color="FFFFFF")
   headers = ["用例编号", "模块", "场景描述", "前置条件", "操作步骤",
         "预期结果", "优先级", "备注"]
for col, header in enumerate(headers, 1):
      cell = ws.cell(row=1, column=col, value=header)
       cell.font = header_font_white
       cell.fill = header_fill
       cell.alignment = Alignment(horizontal="center", vertical="center")
    # 填充用例数据
   for idx, case in enumerate(cases, 1):
       steps = case.get("操作步骤", [])
       if isinstance(steps, list):
          steps_text = "\n".join(f"{i}. {s}" for i, s in enumerate(steps, 1))
       else:
          steps_text = str(steps)
       row_data = [
         f"TC-{idx:04d}",
         case.get("模块", ""),
         case.get("场景描述", ""),
          case.get("前置条件", ""),
         steps_text,
         case.get("预期结果", ""),
         case.get("优先级", "P2"),
         case.get("备注", "")
      ]
      for col, value in enumerate(row_data, 1):
          cell = ws.cell(row=idx + 1, column=col, value=value)
         cell.alignment = Alignment(wrap_text=True, vertical="top")
   # 列宽设置
   col_widths = [12, 15, 30, 25, 40, 30, 8, 15]
    for col, width in enumerate(col_widths, 1):
        ws.column_dimensions[chr(64 + col)].width = width
    # --- Sheet2: 覆盖率自查报告 ---
   if coverage_gaps:
       ws2 = wb.create_sheet("覆盖率自查")
        gap_headers = ["PRD段落", "遗漏说明", "建议补充"]
       for col, header in enumerate(gap_headers, 1):
          cell = ws2.cell(row=1, column=col, value=header)
         cell.font = header_font_white
         cell.fill = PatternFill(start_color="ED7D31", end_color="ED7D31", fill_type="solid")
         cell.alignment = Alignment(horizontal="center")
      for idx, gap in enumerate(coverage_gaps, 2):
         ws2.cell(row=idx, column=1, value=gap.get("prd段落", ""))
         ws2.cell(row=idx, column=2, value=gap.get("遗漏说明", ""))
         ws2.cell(row=idx, column=3, value=gap.get("建议", ""))
         for col in range(1, 4):
              ws2.cell(row=idx, column=col).alignment = Alignment(wrap_text=True, vertical="top")
     ws2.column_dimensions["A"].width = 40
     ws2.column_dimensions["B"].width = 35
     ws2.column_dimensions["C"].width = 35
   wb.save(output_path)
   print(f"\n[INFO] Excel已导出: {output_path}")
# ============================================================
# 分批处理逻辑(S4-03:超过BATCH_SIZE自动分批)
# ============================================================
def generate_cases_batched(dimensions: list, history_ref: list) -> list:
    """
   按维度分批生成用例。
   超过BATCH_SIZE个测试点就分批,结果合并。
   防止大模型注意力衰减导致后面的测试点被"遗忘"。
   """
   # 展开所有测试点
    all_points = []
   for dim in dimensions:
      for point in dim.get("测试点", []):
         all_points.append({"维度": dim["维度"], "测试点": point})
    print(f"\n[INFO] 共 {len(all_points)} 个测试点,BATCH_SIZE={BATCH_SIZE}")
   # 分批
   if len(all_points) <= BATCH_SIZE:
      batches = [all_points]
   else:
       batches = [all_points[i:i + BATCH_SIZE]
          for i in range(0, len(all_points), BATCH_SIZE)]
      print(f"[INFO] 测试点超过{BATCH_SIZE}个,分 {len(batches)} 批处理")
   # 逐批生成
   all_cases = []
   for i, batch in enumerate(batches):
       print(f"\n[INFO] 正在处理第 {i+1}/{len(batches)} 批...")
      input_data = json.dumps({
         "dimensions": batch,
         "history_cases": history_ref
      }, ensure_ascii=False)
      result = call_llm_json(PROMPT_GENERATE_CASES, input_data)
       batch_cases = result.get("cases", [])
       all_cases.extend(batch_cases)
       print(f"[INFO] 第 {i+1} 批生成 {len(batch_cases)} 条用例")
    return all_cases
# ============================================================
# 主流程:完整7步链路
# ============================================================
def generate_test_cases(prd_text: str, project_name: str,
          output_excel: str = None,
       write_to_platform: bool = False) -> list:
   """
   测试用例生成器主流程。
   完整链路:
   1. 输入PRD → 拆测试维度(人工确认)
   2. 搜历史用例 → 筛选相关的作为参考
   3. 按维度+历史参考 → 写具体用例(自动分批)
   4. 覆盖率自查 → 标注PRD中未被覆盖的段落
   5. 人工补充 → 补"不确定区域"
   6. 按团队模板 → 导出Excel
   7. 调API → 写入测试平台(确认后写入)
   Args:
      prd_text: PRD需求文档文本
      project_name: 项目/模块名称
      output_excel: Excel输出路径(为空则不导出)
      write_to_platform: 是否写入测试平台
   Returns:
      生成的测试用例列表
   """
   print(f"\n{'#'*60}")
   print(f"# 测试用例生成器启动")
   print(f"# 项目: {project_name}")
   print(f"# PRD长度: {len(prd_text)} 字符")
   print(f"{'#'*60}")
   # ============================================================
   # Step 1: 拆测试维度
   # ============================================================
   print("\n--- Step 1/7: 拆测试维度 ---")
   while True:
      result = call_llm_json(PROMPT_SPLIT_DIMENSIONS,        prd_text)
      dimensions = result.get("dimensions", [])
      # 展示拆分结果
      display = []
      for dim in dimensions:
          points = dim.get("测试点", [])
         display.append(f"【{dim['维度']}】{' | '.join(points) if points else '(空)'}")
      ok = confirm("测试维度拆分", "\n".join(display))
       if ok:
         break
      print("[INFO] 重新拆分维度...")
   total_points = sum(len(d.get("测试点", [])) for d in dimensions)
   print(f"\n[INFO] 维度拆分完成: {len(dimensions)}个维度, {total_points}个测试点")
   # ============================================================
   # Step 2: 搜索历史用例作为参考
   # ============================================================
   print("\n--- Step 2/7: 搜索历史用例 ---")
   history_ref = []
   try:
      platform = TestPlatformAPI(PLATFORM_BASE_URL, PLATFORM_TOKEN)
      raw_history = platform.search_cases(module_name=project_name, limit=HISTORY_CASE_LIMIT)
      # 转换为精简格式,避免token浪费
      history_ref = transform_history_for_llm(raw_history)
      print(f"[INFO] 找到 {len(raw_history)} 条历史用例,精简后取 {len(history_ref)} 条作为参考")
    except Exception as e:
      print(f"[WARN] 获取历史用例失败(不影响主流程): {e}")
      print("[INFO] 将不使用历史参考,仅基于PRD生成")
   # ============================================================
   # Step 3: 按维度 + 历史参考 → 写具体用例(自动分批)
   # ============================================================
    print("\n--- Step 3/7: 生成测试用例 ---")
   all_cases = generate_cases_batched(dimensions, history_ref)
   print(f"\n[INFO] 共生成 {len(all_cases)} 条测试用例")
    # ============================================================
   # Step 4: 覆盖率自查
   # ============================================================
   print("\n--- Step 4/7: 覆盖率自查 ---")
    coverage_input = json.dumps({
      "prd": prd_text,
      "test_cases": all_cases
   }, ensure_ascii=False)
   coverage_result = call_llm_json(PROMPT_COVERAGE_CHECK,    coverage_input)
   gaps = coverage_result.get("gaps", [])
   coverage_rate = coverage_result.get("coverage_rate", "未知")
    print(f"[INFO] 预估覆盖率: {coverage_rate}")
   if gaps:
       print(f"[INFO] 发现 {len(gaps)} 处遗漏:")
      for i, gap in enumerate(gaps, 1):
          print(f" {i}. PRD段落: {gap.get('prd段落', '')[:50]}...")
          print(f" 遗漏说明: {gap.get('遗漏说明', '')}")
         print(f" 建议: {gap.get('建议', '')}")
    else:
       print("[INFO] 未发现明显遗漏")
    # ============================================================
    # Step 5: 人工补充(不确定区域 + 遗漏补充)
   # ============================================================
   print("\n--- Step 5/7: 人工补充 ---")
   # 标注不确定区域
   uncertainty_input = json.dumps({
      "prd": prd_text,
      "test_cases": all_cases
   }, ensure_ascii=False)
   uncertainty_result = call_llm_json(PROMPT_UNCERTAINTY,    uncertainty_input)
    uncertainties = uncertainty_result.get("uncertainties", [])
   if uncertainties:
      print(f"\n[AI标注的"不确定区域"](共{len(uncertainties)}处):")
       for i, u in enumerate(uncertainties, 1):
          print(f" {i}. 区域: {u.get('区域', '')}")
          print(f" 原因: {u.get('原因', '')}")
         print(f" 建议: {u.get('建议操作', '')}")
    print(f"\n[覆盖率遗漏](共{len(gaps)}处):")
   for i, gap in enumerate(gaps, 1):
       print(f" {i}. {gap.get('prd段落', '')[:60]}")
      print(f" 建议: {gap.get('建议', '')}")
    # 展示生成的用例供确认
   case_display = []
   for idx, case in enumerate(all_cases, 1):
       case_display.append(
          f"{idx}. [{case.get('优先级', 'P2')}] {case.get('场景描述',           '')} "
         f"({case.get('模块', '')})"
       )
   ok = confirm(
      "用例确认(请检查后决定是否继续)",
       "\n".join(case_display) + f"\n\n覆盖率: {coverage_rate}"
       f"\n遗漏: {len(gaps)}处 | 不确定: {len(uncertainties)}处"
    )
    if not ok:
       print("[INFO] 用例需要调整,重新生成...")
       # 这里可以选择重新生成或让用户手动编辑后继续
      # 简化处理:直接退出,用户修改PRD后重跑
      print("[提示] 如需调整,请修改PRD后重新运行,或手动编辑生成的用例")
       return all_cases
   # ============================================================
   # Step 6: 导出Excel
    # ============================================================
    if output_excel:
      print(f"\n--- Step 6/7: 导出Excel ---")
      export_to_excel(all_cases, output_excel, gaps)
       # ============================================================
      # Step 7: 写入测试平台(确认后写入)
       # ============================================================
   if write_to_platform:
      print("\n--- Step 7/7: 写入测试平台 ---")
      ok = confirm("写入测试平台",
         f"即将写入 {len(all_cases)} 条用例到测试平台\n"
         f"f"项目: {project_name}\n"
         f"f"平台: {PLATFORM_BASE_URL}\n\n"
         f"f"确认写入?")
    if not ok:
          print("[INFO] 跳过写入平台,用例已导出到本地")
         return all_cases
    # 数据转换:大模型格式 → 平台API格式
    platform_cases = transform_cases_for_platform(all_cases,     project_name)
     print(f"[INFO] 数据转换完成: {len(platform_cases)} 条用例待写入")
    # 批量写入
    platform = TestPlatformAPI(PLATFORM_BASE_URL, PLATFORM_TOKEN)
    results = platform.batch_create_cases(platform_cases)
    # 统计结果
     success_count = sum(1 for r in results if r["status"] == "success")
    failed_count = len(results) - success_count
     print(f"\n[结果] 写入完成: 成功 {success_count} 条, 失败 {failed_count} 条")
    # 打印平台链接
    for r in results:
         if r["status"] == "success" and r.get("url"):
               print(f" - {r['场景描述']}: {r['url']}")
   print(f"\n{'#'*60}")
  print(f"# 测试用例生成完成!")
   print(f"# 共 {len(all_cases)} 条用例")
   print(f"# 覆盖率: {coverage_rate}")
  if output_excel:
   print(f"# Excel: {output_excel}")
     print(f"{'#'*60}")
   return all_cases
# ============================================================
# 命令行入口
# ============================================================
def main():
     parser = argparse.ArgumentParser(description="测试用例生成器")
     parser.add_argument("--prd", required=True, help="PRD文件路径")
     parser.add_argument("--project", required=True, help="项目/模块名称")
     parser.add_argument("--excel", default=None, help="Excel输出路径(不指定则不导出)")
     parser.add_argument("--platform", action="store_true", help="是否写入测试平台")
      args = parser.parse_args()
      # 读取PRD
     prd_path = Path(args.prd)
      if not prd_path.exists():
            print(f"[ERROR] PRD文件不存在: {prd_path}")
            return
     prd_text = prd_path.read_text(encoding="utf-8")
     if not prd_text.strip():
            print("[ERROR] PRD文件为空")
            return
      # 默认Excel输出路径
      excel_path = args.excel or f"测试用例_{args.project}_{int(time.time())}.xlsx"
      # 运行
     generate_test_cases(
           prd_text=prd_text,
           project_name=args.project,
           output_excel=excel_path,
           write_to_platform=args.platform
     )
if __name__ == "__main__":
            main()

一句话总结:造用例生成器最大的坑不是技术跑不通,是"技术上跑通了但没人用"——补上团队模板、历史参考、覆盖率自查、人工补充四个环节,工具从"能生成"变成"能用"。AI自查不需要完美,比你手动检查快就够了。

   
9   次浏览       1 次
相关文章

基于图卷积网络的图深度学习
自动驾驶中的3D目标检测
工业机器人控制系统架构介绍
项目实战:如何构建知识图谱
 
相关文档

5G人工智能物联网的典型应用
深度学习在自动驾驶中的应用
图神经网络在交叉学科领域的应用研究
无人机系统原理
相关课程

人工智能、机器学习&TensorFlow
机器人软件开发技术
人工智能,机器学习和深度学习
图像处理算法方法与实践

最新活动计划
知识图谱.本体论.RAG大模型 8-22[在线]
企业架构方法与实践 8-27[深圳]
FDE(前沿部署工程师)实践指南 9-8[北京]
AI智能体开发技术实践 9-17[厦门]
UAF架构体系与实践 9-22[北京]
MBSE(基于模型的系统工程)9-29[北京]
 
 
最新文章
AIGC技术与应用全解析
详解知识图谱的构建全流程
大模型升级与设计之道
自动驾驶和辅助驾驶系统
ROS机器人操作系统底层原理
最新课程
人工智能,机器学习和深度学习
人工智能与机器学习应用实战
人工智能-图像处理和识别
人工智能、机器学习& TensorFlow+Keras框架实践
人工智能+Python+大数据
成功案例
某综合性科研机构 人工智能与机器学习
某银行 人工智能+Python+大数据
北京 人工智能、机器学习& TensorFlow
某领先数字地图提供商 Python数据分析
中国移动 人工智能、机器学习和深度学习