基于AI大模型,将自然语言需求自动转化为结构化测试用例

当下的AI技术迅猛渗透到各领域,对软件测试也造成不小的冲击。对测试从业者而言,无需畏惧 AI 的冲击,更应聚焦自身核心能力的提升。

以下分享了如何利用AI(通义大模型),将日常需求转化为测试用例,并自动生成到AgileTC测试用例管理平台中。

(一)核心思路

1、调用AI生成测试用例

用户输入PRD文本 → 构造Prompt → 调用DashScope API → 获取AI返回的JSON

python 复制代码
prd_text = input("请输入需求描述: ").strip()
ai_generator = QwenAIGenerator(api_key="")   #通义api的key
raw_test_cases = ai_generator.generate_from_prd(prd_text)
print("AI返回的json:",raw_test_cases)
python 复制代码
class QwenAIGenerator:
    """使用通义千问API生成测试用例"""

    def __init__(self, api_key: str):
        self.api_key = api_key
        dashscope.api_key = api_key

    def generate_from_prd(self, prd_text: str) -> List[TestCase]:
        """
        通过通义千问API生成测试用例
        要求模型返回严格的JSON格式,便于解析
        """
        prompt = f"""
        你是一个资深测试工程师。请根据以下需求文档(PRD)内容,生成测试用例。

        需求内容:
        {prd_text} 

        请严格按照以下JSON格式输出,不要包含任何其他解释或文本,只输出json数组:
        [
          {{
            "title": "用例标题",
            "steps": "步骤1\n步骤2",
            "expected": "结果1\n结果2",
            "precondition": "前置条件",
            "priority": "P0", 
            "module": "所属模块",
            "tags": ["标签1", "标签2"]
          }}
        ]
        """

        try:
            # 调用通义千问API
            response = dashscope.Generation.call(
                model='qwen-plus',
                prompt=prompt,
                temperature=0.5
            )

            # 提取模型返回的文本
           
            content = response.output['text']
            print(f"🤖 AI原始返回: {content}")  # 调试用:打印AI返回的原始内容


        except Exception as e:
            print(f"❌ 解析或调用异常: {e}")
            return []

    

2、解析AI返回结果

AI返回的JSON字符串 → json.loads() 解析 → 字典列表 ListDict

python 复制代码
# 提取模型返回的文本
                if response.status_code == 200:
                content = response.output['text']
                print(f"🤖 AI原始返回: {content}")  # 调试用:打印AI返回的原始内容

                # 清洗AI返回内容,提取JSON部分
                content = self._extract_json(content)

                # 尝试解析JSON
                case_dicts = json.loads(content)

                # 确保每个字典的字段完整
                for case_dict in case_dicts:
                    case_dict.setdefault("title", "未命名用例")
                    case_dict.setdefault("steps", "")
                    case_dict.setdefault("expected", "")
                    case_dict.setdefault("precondition", "")
                    case_dict.setdefault("priority", "P2")
                    case_dict.setdefault("module", "默认模块")
                    # 确保tags是列表
                    tags = case_dict.get("tags", [])
                    if isinstance(tags, str):
                        case_dict["tags"] = [tags]

                return case_dicts
            else:
                print(f"❌ API调用失败: {response.message}")
                return []

 def _extract_json(self, text: str) -> str:
        """
        从AI返回的文本中提取纯JSON内容
        AI可能会返回 ```json [...] ``` 这样的格式
        """
        import re

        # 尝试匹配 ```json ... ``` 代码块
        match = re.search(r'```(?:json)?\s*\n?(.*?)\n?\s*```', text, re.DOTALL)
        if match:
            return match.group(1).strip()

        # 尝试匹配纯JSON数组
        match = re.search(r'(\[.*\])', text, re.DOTALL)
        if match:
            return match.group(1).strip()

        # 都没有匹配到,返回原文
        return text.strip()

3、转换为AgileTC思维导图格式

字典列表 → 构建思维导图树 → 输出AgileTC格式的JSON字符串

方法 作用
build_case_content(test_points) 主入口,遍历测试点列表,构建完整的思维导图树
_create_text_node(label, text) 创建叶子节点,如"标签: 冒烟测试"
_create_list_node(label, text) 创建带子节点的父节点,按换行符拆分,每行一个子节点
_generate_id() 为每个节点生成唯一ID(UUID截取)
python 复制代码
class MindMapBuilder:
    """构建AgileTC需要的思维导图格式"""

    def build_case_content(self, test_points: List[Dict]) -> str:
        """
        构建思维导图格式的caseContent
        test_points: 包含多个测试点的列表,每个测试点是一个字典
        """
        # 构建完整的思维导图结构
        mindmap = {
            "root": {
                "data": {
                    "id": self._generate_id(),
                    "created": int(time.time() * 1000),
                    "text": test_points[0].get("module", "测试用例")  # 中心主题使用模块名或默认名
                },
                "children": self._build_all_children(test_points)
            },
            "template": "default",
            "theme": "fresh-blue",
            "version": "1.4.43",
            "base": 0
        }
        return json.dumps(mindmap, ensure_ascii=False)

    def _build_all_children(self, test_points: List[Dict]) -> List[Dict]:
        """
        构建所有测试点的子节点
        每个测试点作为一个大的分支(父节点),下面包含步骤和预期
        """
        children = []

        for point in test_points:
            # 1. 创建测试点分支(父节点)
            point_node = {
                "data": {
                    "id": self._generate_id(),
                    "created": int(time.time() * 1000),
                    "text": f"【{point.get('priority', 'P2')}】{point.get('title', '无标题')}"
                },
                "children": []
            }

            # 2. 添加步骤子节点
            if point.get("steps"):
                steps_node = self._create_list_node("执行步骤", point["steps"])
                point_node["children"].append(steps_node)

            # 3. 添加预期子节点
            if point.get("expected"):
                expected_node = self._create_list_node("预期结果", point["expected"])
                point_node["children"].append(expected_node)

            # 4. 添加标签(如果需要)
            if point.get("tags"):
                tags_node = self._create_text_node("标签", ", ".join(point["tags"]))
                point_node["children"].append(tags_node)

            children.append(point_node)

        return children

    def _generate_id(self) -> str:
        """
        生成唯一节点ID
        AgileTC使用类似UUID的格式作为节点标识
        """
        return str(uuid.uuid4()).replace("-", "")[:20]

    def _create_text_node(self, label: str, text: str) -> dict:
        """
        创建单个文本节点
        用于展示标签、前置条件等单行文本信息

        生成的结构示例:
        标签: 冒烟测试, 登录模块
        """
        return {
            "data": {
                "id": self._generate_id(),
                "created": int(time.time() * 1000),
                "text": f"{label}: {text}"
            },
            "children": []
        }

    def _create_list_node(self, label: str, text: str) -> dict:
        """
        创建列表节点(带子节点)
        用于展示步骤、预期结果等多行信息
        每一行变成一个子节点

        生成的结构示例:
        执行步骤
          ├── 1. 打开登录页面
          ├── 2. 输入手机号
          └── 3. 点击获取验证码
        """
        # 按换行符拆分,过滤空行
        lines = [line.strip() for line in text.split("\n") if line.strip()]

        # 构建子节点列表
        children = []
        for line in lines:
            children.append({
                "data": {
                    "id": self._generate_id(),
                    "created": int(time.time() * 1000),
                    "text": line
                },
                "children": []
            })

        # 返回带子节点的父节点
        return {
            "data": {
                "id": self._generate_id(),
                "created": int(time.time() * 1000),
                "text": label
            },
            "children": children
        }
python 复制代码
mindmap_builder = MindMapBuilder()

case_data = {
        "productLineId": AGILETC_CONFIG["productLineId"],
        "channel": AGILETC_CONFIG["channel"],
        "creator": AGILETC_CONFIG["username"],
        "caseType": 1,
        # 用例标题可以取第一个用例的标题,或者自定义
        "title": f"综合测试用例:{raw_test_cases[0].get('module', '功能测试')}",
        "description": f"该用例包含 {len(raw_test_cases)} 个测试点",

        # 🔴 关键:将所有测试点传入 builder
        "caseContent": mindmap_builder.build_case_content(raw_test_cases),

        "priority": 1,
        "status": 1,
        "folderId": 1,
        "module": raw_test_cases[0].get("module", "默认模块"),
        "tags": "AI生成,综合用例",
        "createTime": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "updateTime": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "projectId": AGILETC_CONFIG["project_id"],
        "autoStatus": 0,
        "reviewStatus": 0,
        "version": "1.0",
        "bizId": AGILETC_CONFIG["bizId"],
        "creator": AGILETC_CONFIG["creator"],
        "productLineId": AGILETC_CONFIG["productLineId"]
    }

4、对接测试用例平台

AgileTC格式JSON → 写入文件 / 调用AgileTC接口导入

python 复制代码
AGILETC_CONFIG = {
    "base_url": "https://testacthuyitool.jidiandian.cn",
    "username": "",  # 你的用户名
    "bizId": "-1",  # 默认文件夹ID
    "project_id": 1,
    "channel": 1,
    "caseType": 0,
    "creator": "",
    "productLineId": 1
}

class AgileTCClient:
    def __init__(self, config: Dict):
        self.base_url = config["base_url"].rstrip('/')
        self.username = config["username"]
        self.bizId = config["bizId"]
        self.project_id = config["project_id"]
        self.channel = config["channel"]
        self.caseType = config["caseType"]
        self.creator = config["creator"]
        self.productLineId = config["productLineId"]
        self.session = requests.Session()
        self.session.cookies.set('username', self.username)
        self.headers = {
            "User-Agent": "Mozilla/5.0",
            "Content-Type": "application/json;charset=UTF-8",
            "Accept": "application/json, text/plain, */*",
        }

    def create_test_case(self, case_data: Dict) -> Dict:
        """创建测试用例 - 针对你的API响应格式修复"""
        url = f"{self.base_url}/api/case/create"
        print(f"📤 创建用例: {case_data.get('title')}")

        try:
            response = self.session.post(url, json=case_data, headers=self.headers, timeout=10)
            print(f"📡 响应状态码: {response.status_code}")
            print(f"📡 响应内容: {response.text[:200]}...")

            # 解析JSON响应
            result = response.json()
            print(f"📡 解析后的JSON: {result}")

            # 🔴 关键修复:正确处理data字段
            if result.get("code") == 200:
                data_value = result.get("data")

                # 检查data的类型
                if isinstance(data_value, dict):
                    # data是字典,包含id字段
                    case_id = data_value.get("id", "未知")
                    print(f"✅ 创建成功!用例ID: {case_id}")
                elif isinstance(data_value, (int, str)):
                    # data是整数或字符串,直接就是用例ID
                    case_id = data_value
                    print(f"✅ 创建成功!用例ID: {case_id}")
                else:
                    # 其他类型
                    case_id = "未知"
                    print(f"✅ 创建成功!data字段: {data_value}")

                return {
                    "success": True,
                    "code": 200,
                    "msg": result.get("msg", "成功"),
                    "data": {"id": case_id} if not isinstance(data_value, dict) else data_value,
                    "raw_response": result
                }
            else:
                error_msg = result.get("msg", f"错误码: {result.get('code')}")
                print(f"❌ 创建失败: {error_msg}")
                return {
                    "success": False,
                    "code": result.get("code"),
                    "msg": error_msg,
                    "error": error_msg
                }

        except Exception as e:
            print(f"💥 请求异常: {e}")
            return {"success": False, "error": str(e)}


client = AgileTCClient(AGILETC_CONFIG)

result = client.create_test_case(case_data)
    if result.get("success"):
        print(f"✅ 所有测试点已合并创建成功!")
    else:
        print(f"❌ 创建失败")

(二)注意点

模型名称 定位 说明
qwen-max 旗舰 效果最好,最贵
qwen-plus 均衡 效果和成本平衡,推荐
qwen-turbo 快速 速度快,成本低

博主一开始用的 qwen-max 效果最好但贵,很快就账号欠费了,后来换成 qwen-plus 性价比更高,生成测试用例完全够用。

(三)运行效果

总结:用AI大模型把自然语言的需求文档,自动转成结构化的测试用例,再转换成AgileTC思维导图格式,实现从需求到用例的一键生成。

相关推荐
金智维科技官方1 小时前
AI驱动的应付账款自动化,落地时要拆解哪些流程?
运维·人工智能·自动化
蓝速科技1 小时前
蓝速科技 AI 数字人渲染显卡选型与部署指南
人工智能·科技
带刺的坐椅1 小时前
把 OpenAPI 接入 Agent Harness:零代码让 Agent 听懂你的 REST API
java·ai·llm·agent·solon·restapi·openapi
电化学仪器白超1 小时前
低阻域 ADC 与参考源选型理论分析
人工智能·python·单片机·嵌入式硬件·自动化
AI科技星1 小时前
超复数全域经济周期场与信息谱场——金融与密码学底层理论重构《0·1·∞三元一体全域超复数统一场论》系列全集(六一字不漏完整合订终版)
人工智能·算法·金融·密码学·拓扑学·乖乖数学·全域数学
香蕉鼠片1 小时前
机器学习经典任务
深度学习·ai
开发者如是说2 小时前
Vibe Code 了产品却无人下载?我写了个工具来帮你
人工智能·程序员·产品
AI科技星2 小时前
《01无穷全域信息场论:算子G与宇宙本体高维完备公理大典》
人工智能·python·算法·金融·乖乖数学·全域数学
战场小包2 小时前
AI 写代码越来越快,慢下来的那个人变成了我
人工智能·agent
sunywz2 小时前
【AI RAG知识库】05.【导入】【节点5】node_item_name_recognition
人工智能