当前位置: 首页 > news >正文

Deepseek+python自动生成禅道测试用例

1. 核心功能

  • AI驱动:使用DeepSeek AI模型自动生成测试用例
  • 可配置性强:支持自定义测试场景、字段信息、业务背景和测试要求
  • 智能解析:自动解析AI生成的文本内容为结构化测试用例数据
  • 多样化输出:支持界面展示和Excel导出

2. 架构设计

主要类模块:

  • TestCaseGenerator:AI测试用例生成器

     - 调用DeepSeek API生成测试用例- 支持动态提示词构建
    
  • TestCaseParser:测试用例解析器

    - 使用正则表达式解析AI响应
    - 转换为结构化数据格式
    
  • TestCaseExporter:测试用例导出器

    - 导出为Excel文件
    - 生成统计报告
    
  • TestCaseGUI:图形用户界面

    - 基于tkinter的桌面应用
    - 多标签页设计(配置/结果)
    

3.代码

from openai import OpenAI
import pandas as pd
import re
from datetime import datetime
import os
from typing import List, Dict, Optional
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext, filedialog
import threadingclass TestCaseGenerator:"""可配置的测试用例生成器"""def __init__(self, api_key: str, base_url: str = "https://api.deepseek.com/v1"):self.client = OpenAI(api_key=api_key, base_url=base_url)self.model = "deepseek-chat"self.temperature = 0.3def generate_prompt(self, test_scenario: str, fields_info: Dict = None,business_context: str = "", test_requirements: List[str] = None) -> str:"""动态生成提示词Args:test_scenario: 测试场景描述fields_info: 字段信息字典business_context: 业务背景test_requirements: 测试要求列表"""format_template = """
【输出格式】
1. 用例标题:[查询组合] - [测试目标]
2. 所属模块:/新航发售后2.0(#798)
3. 前置条件:[测试执行前需要满足的条件]
4. 测试步骤:步骤1:[具体操作步骤]步骤2:[具体操作步骤]
5. 预期结果:[期望的系统响应]
6. 优先级:[高/中/低]
7. 用例类型:[功能测试/性能测试/安全相关]"""prompt = f"""
你是一位专业的软件测试工程师,请为以下场景设计测试用例。【测试场景】
{test_scenario}"""if fields_info:prompt += "【字段信息】\n"for field, info in fields_info.items():prompt += f"{field}{info}\n"prompt += "\n"if business_context:prompt += f"【业务背景】\n{business_context}\n\n"if test_requirements:prompt += "【测试要求】\n"for i, req in enumerate(test_requirements, 1):prompt += f"{i}. {req}\n"prompt += "\n"prompt += f"{format_template}\n请提供高质量测试用例,确保覆盖各种场景。"return promptdef generate_test_cases(self, prompt: str, max_tokens: int = 4000) -> Optional[str]:"""调用AI生成测试用例Args:prompt: 提示词max_tokens: 最大token数Returns:AI生成的内容或None"""try:response = self.client.chat.completions.create(model=self.model,messages=[{"role": "system","content": """你是一位专业的软件测试工程师,擅长设计测试用例。你输出的内容将被程序自动解析,因此格式极其重要。请严格按照指定格式输出,不要添加任何额外说明。"""},{"role": "user", "content": prompt}],temperature=self.temperature,max_tokens=max_tokens)ai_content = response.choices[0].message.contentprint("AI生成的测试用例:")print(ai_content)return ai_contentexcept Exception as e:print(f"错误类型: {type(e).__name__}")print(f"错误信息: {str(e)}")return Noneclass TestCaseParser:"""测试用例解析器"""@staticmethoddef parse_test_cases(ai_response: str) -> List[Dict]:"""解析AI生成的测试用例文本,转换为结构化数据"""if not ai_response:print("AI响应为空")return []# 先去除开头的说明文字,从第一个用例标题开始截取first_case_index = ai_response.find("1. 用例标题:")if first_case_index == -1:print("未找到测试用例开始标记")return []# 提取从第一个用例开始的所有内容cases_content = ai_response[first_case_index:]# 按照用例标题分割不同的测试用例test_cases_raw = re.split(r'(?=1\.\s*用例标题:)', cases_content)# 过滤掉空的内容test_cases_raw = [case.strip() for case in test_cases_raw if case.strip()]test_cases = []for case_text in test_cases_raw:if not case_text.strip():continuetest_case = TestCaseParser._parse_single_case(case_text)if test_case:test_cases.append(test_case)return test_cases@staticmethoddef _parse_single_case(case_text: str) -> Optional[Dict]:"""解析单个测试用例"""# 提取各个字段,使用更灵活的正则表达式title_match = re.search(r'1\.\s*用例标题[::]\s*(.+)', case_text)module_match = re.search(r'2\.\s*所属模块[::]\s*(.+)', case_text)# 前置条件可能有多行precondition_match = re.search(r'3\.\s*前置条件[::]?\s*(.*?)(?=\n\d+\.|\Z)', case_text, re.DOTALL)# 测试步骤可能有多行steps_match = re.search(r'4\.\s*测试步骤[::]?\s*(.*?)(?=\n\d+\.|\Z)', case_text, re.DOTALL)# 预期结果可能有多行expected_match = re.search(r'5\.\s*预期结果[::]?\s*(.*?)(?=\n\d+\.|\Z)', case_text, re.DOTALL)priority_match = re.search(r'6\.\s*优先级[::]\s*(.+)', case_text)type_match = re.search(r'7\.\s*用例类型[::]\s*(.+)', case_text)test_case = {'用例标题': title_match.group(1).strip() if title_match else '','所属模块': module_match.group(1).strip() if module_match else '/新航发售后2.0(#798)','前置条件': precondition_match.group(1).strip() if precondition_match else '','步骤': steps_match.group(1).strip() if steps_match else '','预期': expected_match.group(1).strip() if expected_match else '','优先级': priority_match.group(1).strip() if priority_match else '中','用例类型': type_match.group(1).strip() if type_match else '功能测试'}# 验证必要字段if not test_case['用例标题']:return Nonereturn test_caseclass TestCaseExporter:"""测试用例导出器"""@staticmethoddef export_to_excel(test_cases: List[Dict], filename: str = None) -> str:"""导出测试用例到Excel文件Args:test_cases: 测试用例列表filename: 文件名Returns:生成的文件路径"""if not filename:timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")filename = f'测试用例_{timestamp}.xlsx'df = pd.DataFrame(test_cases)df.to_excel(filename, index=False)print(f"测试用例已保存至 {filename}")return filename@staticmethoddef generate_report(test_cases: List[Dict]) -> Dict:"""生成测试用例统计报告"""if not test_cases:return {}# 按优先级统计priority_stats = {}type_stats = {}for case in test_cases:priority = case.get('优先级', '中')priority_stats[priority] = priority_stats.get(priority, 0) + 1case_type = case.get('用例类型', '功能测试')type_stats[case_type] = type_stats.get(case_type, 0) + 1return {'total': len(test_cases),'priorities': priority_stats,'types': type_stats}class TestCaseGUI:"""测试用例生成器图形界面"""def __init__(self):self.root = tk.Tk()self.root.title("AI测试用例生成器")self.root.geometry("1000x700")# 设置API密钥self.api_key = "deepseek接口秘钥"self.setup_ui()def setup_ui(self):"""设置用户界面"""# 创建笔记本控件(标签页)notebook = ttk.Notebook(self.root)notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)# 配置标签页config_frame = ttk.Frame(notebook)notebook.add(config_frame, text="配置信息")# 结果标签页result_frame = ttk.Frame(notebook)notebook.add(result_frame, text="生成结果")# 设置配置标签页self.setup_config_tab(config_frame)# 设置结果标签页self.setup_result_tab(result_frame)# 状态栏self.status_var = tk.StringVar()self.status_var.set("就绪")status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)status_bar.pack(side=tk.BOTTOM, fill=tk.X)def setup_config_tab(self, parent):"""设置配置标签页"""# 主框架main_frame = ttk.Frame(parent)main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)# 测试场景ttk.Label(main_frame, text="测试场景:").grid(row=0, column=0, sticky=tk.W, pady=5)self.scenario_var = tk.StringVar(value="售后列表页面的多字段组合查询功能")scenario_entry = ttk.Entry(main_frame, textvariable=self.scenario_var, width=40)scenario_entry.grid(row=0, column=1, columnspan=2, sticky=tk.EW, pady=5, padx=(0, 10))# 业务背景ttk.Label(main_frame, text="业务背景:").grid(row=1, column=0, sticky=tk.NW, pady=5)self.context_var = tk.StringVar(value="售后管理系统用于处理客户提交的各类售后服务请求。\n列表页面支持多条件组合查询,帮助客服人员快速定位和处理工单。")context_text = tk.Text(main_frame, height=4, width=40)context_text.grid(row=1, column=1, columnspan=2, sticky=tk.EW, pady=5, padx=(0, 10))context_text.insert(tk.END, self.context_var.get())self.context_text = context_text# 字段信息框架field_frame = ttk.LabelFrame(main_frame, text="字段信息")field_frame.grid(row=2, column=0, columnspan=3, sticky=tk.EW, pady=10)field_frame.columnconfigure(1, weight=1)# 字段信息列表self.field_entries = []self.add_field_entry(field_frame, "售后订单号", "文本输入框")self.add_field_entry(field_frame, "售后类型", "下拉选择框:维修、更换、退货、咨询")self.add_field_entry(field_frame, "申请单号", "文本输入框")self.add_field_entry(field_frame, "售后状态", "下拉选择框:待处理、处理中、已完成、已关闭、已取消")# 添加字段按钮add_field_btn = ttk.Button(field_frame, text="添加字段", command=lambda: self.add_field_entry(field_frame))add_field_btn.pack(pady=5)# 测试要求框架req_frame = ttk.LabelFrame(main_frame, text="测试要求")req_frame.grid(row=3, column=0, columnspan=3, sticky=tk.EW, pady=10)req_frame.columnconfigure(0, weight=1)# 测试要求列表self.req_entries = []default_requirements = ["单字段查询测试:每个字段的独立查询功能验证,字段特定的边界值和异常情况","两两字段组合查询测试:任意两个字段的组合查询,组合查询结果的准确性验证","多字段组合查询测试:三个及以上字段的组合查询,查询条件优先级和逻辑关系验证","查询性能测试:大数据量查询响应时间,复杂条件查询性能","用户体验测试:查询条件清除功能,查询历史记录,查询结果展示","异常场景测试:无匹配结果查询,查询条件冲突,特殊字符处理"]for req in default_requirements:self.add_requirement_entry(req_frame, req)# 添加要求按钮add_req_btn = ttk.Button(req_frame, text="添加要求", command=lambda: self.add_requirement_entry(req_frame))add_req_btn.pack(pady=5)# 按钮框架button_frame = ttk.Frame(main_frame)button_frame.grid(row=4, column=0, columnspan=3, pady=20)# 生成按钮generate_btn = ttk.Button(button_frame, text="生成测试用例", command=self.generate_test_cases_thread)generate_btn.pack(side=tk.LEFT, padx=5)# 重置按钮reset_btn = ttk.Button(button_frame, text="重置", command=self.reset_form)reset_btn.pack(side=tk.LEFT, padx=5)# 配置列权重main_frame.columnconfigure(1, weight=1)def add_field_entry(self, parent, name="", info=""):"""添加字段条目"""row = len(self.field_entries)frame = ttk.Frame(parent)frame.pack(fill=tk.X, pady=2)name_var = tk.StringVar(value=name)info_var = tk.StringVar(value=info)name_entry = ttk.Entry(frame, textvariable=name_var, width=15)name_entry.pack(side=tk.LEFT, padx=(5, 2))info_entry = ttk.Entry(frame, textvariable=info_var, width=30)info_entry.pack(side=tk.LEFT, padx=2)delete_btn = ttk.Button(frame, text="删除", command=lambda: self.delete_field_entry(frame))delete_btn.pack(side=tk.LEFT, padx=2)self.field_entries.append((frame, name_var, info_var))def delete_field_entry(self, frame):"""删除字段条目"""for i, (f, _, _) in enumerate(self.field_entries):if f == frame:self.field_entries.pop(i)breakframe.destroy()def add_requirement_entry(self, parent, text=""):"""添加测试要求条目"""row = len(self.req_entries)frame = ttk.Frame(parent)frame.pack(fill=tk.X, pady=2)text_var = tk.StringVar(value=text)text_entry = ttk.Entry(frame, textvariable=text_var)text_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(5, 2))delete_btn = ttk.Button(frame, text="删除", command=lambda: self.delete_requirement_entry(frame))delete_btn.pack(side=tk.LEFT, padx=2)self.req_entries.append((frame, text_var))def delete_requirement_entry(self, frame):"""删除测试要求条目"""for i, (f, _) in enumerate(self.req_entries):if f == frame:self.req_entries.pop(i)breakframe.destroy()def setup_result_tab(self, parent):"""设置结果标签页"""# 创建文本框和滚动条text_frame = ttk.Frame(parent)text_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)self.result_text = scrolledtext.ScrolledText(text_frame, wrap=tk.WORD)self.result_text.pack(fill=tk.BOTH, expand=True)# 按钮框架button_frame = ttk.Frame(parent)button_frame.pack(fill=tk.X, padx=10, pady=5)# 导出按钮export_btn = ttk.Button(button_frame, text="导出Excel", command=self.export_to_excel)export_btn.pack(side=tk.LEFT, padx=5)# 清空按钮clear_btn = ttk.Button(button_frame, text="清空结果", command=self.clear_results)clear_btn.pack(side=tk.LEFT, padx=5)# 统计信息标签self.stats_var = tk.StringVar()stats_label = ttk.Label(button_frame, textvariable=self.stats_var)stats_label.pack(side=tk.RIGHT, padx=5)def generate_test_cases_thread(self):"""在新线程中生成测试用例"""thread = threading.Thread(target=self.generate_test_cases)thread.daemon = Truethread.start()def generate_test_cases(self):"""生成测试用例"""try:self.status_var.set("正在生成测试用例...")self.root.update()# 收集表单数据test_scenario = self.scenario_var.get()business_context = self.context_text.get("1.0", tk.END).strip()# 收集字段信息fields_info = {}for _, name_var, info_var in self.field_entries:name = name_var.get().strip()info = info_var.get().strip()if name and info:fields_info[name] = info# 收集测试要求test_requirements = []for _, text_var in self.req_entries:req = text_var.get().strip()if req:test_requirements.append(req)# 生成测试用例generator = TestCaseGenerator(api_key=self.api_key)prompt = generator.generate_prompt(test_scenario=test_scenario,fields_info=fields_info if fields_info else None,business_context=business_context,test_requirements=test_requirements if test_requirements else None)ai_content = generator.generate_test_cases(prompt)if ai_content:# 解析测试用例parsed_cases = TestCaseParser.parse_test_cases(ai_content)if parsed_cases:# 保存结果self.generated_cases = parsed_cases# 显示结果self.display_results(parsed_cases)# 显示统计信息report = TestCaseExporter.generate_report(parsed_cases)stats_text = f"总计: {report['total']} | 优先级: {report['priorities']} | 类型: {report['types']}"self.stats_var.set(stats_text)self.status_var.set(f"生成完成,共 {len(parsed_cases)} 个测试用例")messagebox.showinfo("成功", f"成功生成 {len(parsed_cases)} 个测试用例!")else:self.status_var.set("解析测试用例失败")messagebox.showerror("错误", "解析测试用例失败")else:self.status_var.set("生成测试用例失败")messagebox.showerror("错误", "生成测试用例失败")except Exception as e:self.status_var.set(f"发生错误: {str(e)}")messagebox.showerror("错误", f"发生错误: {str(e)}")def display_results(self, cases):"""显示结果"""self.result_text.delete(1.0, tk.END)for i, case in enumerate(cases, 1):self.result_text.insert(tk.END, f"=== 测试用例 {i} ===\n", "title")self.result_text.insert(tk.END, f"用例标题: {case.get('用例标题', '')}\n")self.result_text.insert(tk.END, f"所属模块: {case.get('所属模块', '')}\n")self.result_text.insert(tk.END, f"前置条件: {case.get('前置条件', '')}\n")self.result_text.insert(tk.END, f"测试步骤: {case.get('步骤', '')}\n")self.result_text.insert(tk.END, f"预期结果: {case.get('预期', '')}\n")self.result_text.insert(tk.END, f"优先级: {case.get('优先级', '')}\n")self.result_text.insert(tk.END, f"用例类型: {case.get('用例类型', '')}\n")self.result_text.insert(tk.END, "\n")# 配置标签self.result_text.tag_config("title", foreground="blue", font=("Arial", 10, "bold"))def export_to_excel(self):"""导出到Excel"""if not hasattr(self, 'generated_cases') or not self.generated_cases:messagebox.showwarning("警告", "没有可导出的测试用例")returntry:# 选择保存路径filename = filedialog.asksaveasfilename(defaultextension=".xlsx",filetypes=[("Excel文件", "*.xlsx"), ("所有文件", "*.*")],title="保存测试用例")if filename:filepath = TestCaseExporter.export_to_excel(self.generated_cases, filename)messagebox.showinfo("成功", f"测试用例已保存至:\n{filepath}")self.status_var.set(f"文件已保存: {filepath}")except Exception as e:messagebox.showerror("错误", f"导出失败: {str(e)}")def clear_results(self):"""清空结果"""self.result_text.delete(1.0, tk.END)self.stats_var.set("")if hasattr(self, 'generated_cases'):delattr(self, 'generated_cases')self.status_var.set("结果已清空")def reset_form(self):"""重置表单"""# 重置基本字段self.scenario_var.set("售后列表页面的多字段组合查询功能")self.context_text.delete(1.0, tk.END)self.context_text.insert(tk.END, "售后管理系统用于处理客户提交的各类售后服务请求。\n列表页面支持多条件组合查询,帮助客服人员快速定位和处理工单。")# 清空字段条目for frame, _, _ in self.field_entries[:]:frame.destroy()self.field_entries.clear()# 重新添加默认字段self.add_field_entry(self.field_entries[0][0].master, "售后订单号", "文本输入框")self.add_field_entry(self.field_entries[0][0].master, "售后类型", "下拉选择框:维修、更换、退货、咨询")self.add_field_entry(self.field_entries[0][0].master, "申请单号", "文本输入框")self.add_field_entry(self.field_entries[0][0].master, "售后状态", "下拉选择框:待处理、处理中、已完成、已关闭、已取消")# 清空要求条目for frame, _ in self.req_entries[:]:frame.destroy()self.req_entries.clear()# 重新添加默认要求default_requirements = ["单字段查询测试:每个字段的独立查询功能验证,字段特定的边界值和异常情况","两两字段组合查询测试:任意两个字段的组合查询,组合查询结果的准确性验证","多字段组合查询测试:三个及以上字段的组合查询,查询条件优先级和逻辑关系验证","查询性能测试:大数据量查询响应时间,复杂条件查询性能","用户体验测试:查询条件清除功能,查询历史记录,查询结果展示","异常场景测试:无匹配结果查询,查询条件冲突,特殊字符处理"]req_frame = self.req_entries[0][0].master if self.req_entries else Noneif req_frame:for req in default_requirements:self.add_requirement_entry(req_frame, req)self.status_var.set("表单已重置")def run(self):"""运行GUI"""self.root.mainloop()# 使用示例
def main():# 创建并运行GUIapp = TestCaseGUI()app.run()if __name__ == "__main__":main()

4.运行效果

在这里插入图片描述
在这里插入图片描述

5.优化方向

  • C/S方向-exe文件
  • B/S方向-浏览器页面交互
http://www.dtcms.com/a/341222.html

相关文章:

  • 将集合拆分成若干个batch,并将batch存于新的集合
  • WMS仓储管理系统如何解决仓库的呆滞库存
  • 鸿蒙安卓前端中加载丢帧:ArkWeb分析
  • 第5.7节:awk赋值运算
  • IPSEC安全基础
  • Qt 中最经典、最常用的多线程通信场景
  • TDengine IDMP 运维指南(数据导入导出)
  • WIN10/WIN11:无法下载所有必需的文件 0x80072EE2 0x20000(未解决)
  • C++ std::sort的应用总结
  • Unity 大量子弹和小怪碰撞检测优化
  • GSPO:Towards scalable reinforcement learning for language models
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型和EasyOCR实现汽车牌照动态检测和识别(C#代码,UI界面版)
  • 使用UUP dump制作windows preview镜像
  • 手机、汽车如何实现卫星直连
  • imx6ull-驱动开发篇31——Linux异步通知
  • 玩转QEMU硬件模拟器 - Raspberry Pi OS驱动开发
  • 【项目复盘】【四轴飞行器设计】驱动开发部分
  • Redis 安装教程
  • 【数据结构之二叉树】
  • 【openssl】openssl CA.pl 签发证书操作步骤
  • redis执行lua脚本的原子性和数据库原子性的区别
  • [激光原理与应用-315]:光学设计 - SolidWorks, 光机系统设计的神器,打通光学与机械设计的闭环
  • Tomcat部署与HTTP协议详解
  • 佳维视工业一体机在公共交通系统配套中的应用
  • 疯狂星期四文案网第45天运营日记
  • LTspice仿真电路:(三十五)LED恒流驱动仿真(LT3497)
  • burpsuite+captcha-killer插件识别图片验证码进行爆破
  • AiPy 文档自动化处理实践:从 docx 到结构化 db 的高效转换方案
  • 华为仓颉语言的class(类)初步
  • ES Modules +案例分析