当前位置: 首页 > 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.lryc.cn/news/626929.html

相关文章:

  • 自动化测试用例生成:基于Python的参数化测试框架设计与实现
  • 记一次pnpm start启动异常
  • Spring Boot 3整合Nacos,配置namespace
  • 质谱数据分析环节体系整理
  • Rust 入门 包 (二十一)
  • 内网环境给VSCode安装插件
  • PostgreSQL 流程---更新
  • 基于51单片机自动浇花1602液晶显示设计
  • Notepad++批量转UTF-8脚本
  • 测试DuckDB插件对不同格式xlsx文件的读写效率
  • 基于Pytochvideo训练自己的的视频分类模型
  • 【C++】基础:C++11-14-17常用新特性介绍
  • XR(AR/VR/MR)芯片方案,Soc VS “MCU+协处理器”?
  • 109、【OS】【Nuttx】【周边】效果呈现方案解析:workspaceStorage(下)
  • 【最后203篇系列】034 使用SQLite构建简单的任务管理
  • 解决Docker 无法连接到官方镜像仓库
  • LINUX 820 shell:shift,expect
  • 49 C++ STL模板库18-类模板-pair
  • 双模式 RTMP H.265 播放器解析:从国内扩展到 Enhanced RTMP 标准的演进
  • 深入理解JVM内存结构:从字节码执行到垃圾回收的全景解析
  • 基于单片机智能加湿器/空气加湿器
  • ubuntu系统上的conda虚拟环境导出方便下次安装
  • 计算机毕设Spark项目实战:基于大数据技术的就业数据分析系统Django+Vue开发指南
  • Typescript入门-数组元组讲解
  • CSS3DRenderer+ CSS3DObject实现在 Three.js 中添加文本内容
  • 监听视频是否加载完毕
  • 次短路P2865 [USACO06NOV] Roadblocks G题解
  • KubeBlocks for ClickHouse 容器化之路
  • 【机器学习深度学习】AI大模型高并发挑战:用户负载部署策略
  • OceanBase DBA实战营2期--SQL 关键字限流学习笔记