戳这里查看上期,前面我们拆解了测试用例生成工具的设计思路与技术原理,本篇从界面层和功能清单开始讲落地实现。
本文结合4个核心页面(功能清单页面、生成测试用例页面、导出功能清单页面、导出测试用例页面),基于「双Tab业务拆分+模块化架构」,从界面层、业务层、智能层三个维度,详细说明工具的落地实现逻辑,同时包含核心功能的下载功能设计,确保每个环节可落地、可复用,代码块均添加详细说明,提升可读性。
核心目标:分离「功能清单生成」和「测试用例生成」业务,避免功能混淆,提升用户操作体验;采用解耦设计,便于后续单个Tab功能的修改和维护。
代码说明:使用CustomTkinter组件创建Tab容器,新增两个核心Tab,分别绑定独立的UI初始化方法,确保两个Tab功能互不干扰,适配主窗口尺寸预留合理边距。
# 测试用例生成工具:设计具体实现(细化落地细节)
def init_main_tabs(self):
# 1. 创建主Tab容器(CustomTkinter原生组件,支持切换动画)
self.main_tabview = ctk.CTkTabview(self, width=1460, height=860)
self.main_tabview.pack(fill="both", expand=True, padx=20, pady=10)
# 2. 新增两个核心Tab,绑定独立UI初始化方法
self.func_list_tab = self.main_tabview.add("功能清单生成") # Tab1:功能清单
self.case_gen_tab = self.main_tabview.add("测试用例生成") # Tab2:测试用例
# 3. 分别初始化两个Tab的UI(解耦设计,便于维护)
self.init_func_list_ui() # 功能清单UI初始化
self.init_case_gen_ui() # 测试用例UI初始化
实现细节:
• Tab容器默认支持「点击切换」,无需额外绑定事件;
• 界面尺寸适配主窗口(1500x900),预留20px边距,避免组件溢出;
• 两个Tab的UI初始化方法独立,修改一个Tab不影响另一个的功能。
核心目标:支持模块的「添加/删除/数据同步」,适配灵活的功能清单配置,用户可根据需求新增多个模块,删除无用模块,确保输入的模块数据能同步到全局变量。
代码说明:封装ModuleFrame类(继承自CTkFrame),内置模块名称、页面功能、模板类型等输入组件,提供数据获取、删除回调方法,实现组件与全局数据的同步
class ModuleFrame(ctk.CTkFrame):
def __init__(self, master, delete_callback, index):
super().__init__(master, fg_color="transparent")
self.index = index # 模块索引(用于删除时定位)
self.delete_callback = delete_callback # 删除回调(通知主窗口更新索引)
# 1. 封装5个核心组件(标签+输入框+下拉框+按钮)
self.module_label = ctk.CTkLabel(self, text=f"模块{index+1}名称:", width=100)
self.module_entry = ctk.CTkEntry(self, placeholder_text="例如:一件一案模块", width=200)
self.page_func_label = ctk.CTkLabel(self, text="页面功能:", width=80)
self.page_func_entry = ctk.CTkEntry(self, placeholder_text="例如:首页、步骤模板页面", width=300)
self.template_label = ctk.CTkLabel(self, text="模板类型:", width=80)
self.template_var = ctk.StringVar(value="简单模板")
self.template_option = ctk.CTkOptionMenu(self, values=["全模板", "简单模板"], variable=self.template_var, width=120)
self.remark_label = ctk.CTkLabel(self, text="备注:", width=60)
self.remark_entry = ctk.CTkEntry(self, placeholder_text="可选:特殊说明", width=150)
self.delete_btn = ctk.CTkButton(self, text="删除", command=self.delete, width=60, fg_color="#ff4444")
# 2. 水平布局(side="left"),保证组件排列整齐
for widget in [self.module_label, self.module_entry, self.page_func_label, self.page_func_entry,
self.template_label, self.template_option, self.remark_label, self.remark_entry, self.delete_btn]:
widget.pack(side="left", padx=5)
# 3. 数据通信方法(主窗口通过该方法获取模块数据)
"name": self.module_entry.get().strip(),
"page_func": self.page_func_entry.get().strip(),
"template": self.template_var.get(),
"remark": self.remark_entry.get().strip()
self.destroy() # 销毁当前模块组件
self.delete_callback(self.index) # 调用主窗口的删除回调,传递当前模块索引
关键通信逻辑:
• 主窗口通过get_data()批量获取所有模块数据,用于生成功能清单;
• 模块通过delete_callback回调通知主窗口删除自身,确保界面组件与全局数据同步。
核心目标:实时预览功能清单和测试用例数据,支持双击单元格编辑数据,编辑后同步更新全局数据,同时适配大量数据的滚动查看,提升用户操作便捷性。
代码说明:创建Treeview组件,指定与功能清单匹配的列名,配置列宽、对齐方式,绑定双击编辑事件,添加垂直滚动条,实现数据可视化预览和便捷编辑。
def init_func_preview_tree(self):
self.func_tree = ttk.Treeview(
columns=("功能模块", "页面功能", "功能项", "备注"), # 与功能清单字段一一对应
show="headings", # 只显示表头,隐藏默认第一列
self.func_tree.column("功能模块", width=180, anchor="w") # 左对齐
self.func_tree.column("页面功能", width=250, anchor="w")
self.func_tree.column("功能项", width=400, anchor="w")
self.func_tree.column("备注", width=150, anchor="w")
# 3. 绑定双击编辑事件(回调到edit_func_cell方法)
self.func_tree.bind("<Double-1>", self.edit_func_cell)
func_scrollbar = ttk.Scrollbar(self.func_preview_tab, orient="vertical", command=self.func_tree.yview)
self.func_tree.configure(yscrollcommand=func_scrollbar.set)
# 5. 布局(Treeview左填充满,滚动条靠右)
self.func_tree.pack(side="left", fill="both", expand=True, padx=(10, 0), pady=10)
func_scrollbar.pack(side="right", fill="y", padx=(0, 10), pady=10)
双击编辑实现核心:
-
通过identify_region识别点击区域(仅单元格可编辑);
-
动态创建ttk.Entry输入框,编辑完成后同步数据到全局变量global_function_list。
核心目标:合并用户输入的页面功能与预设的通用模板功能,生成标准化、无冗余的功能清单,同时过滤无效数据,补充关键信息(功能项数量),为后续测试用例生成提供规范数据。
代码说明:先校验必填项(系统名称),再遍历所有模块,根据选择的模板类型获取通用功能,与页面功能合并去重,最后格式化数据,生成符合导出模板字段的功能清单,存入全局变量。
def generate_full_function(self):
global global_system_name, global_modules, global_function_list
global_system_name = self.system_name_entry.get().strip()
if not global_system_name:
messagebox.showwarning("警告", "请输入系统名称!")
for frame in self.module_frames:
module_data = frame.get_data()
if not module_data["name"] and not module_data["page_func"] and not module_data["remark"]:
# 3. 根据模板类型获取通用功能(全模板/简单模板)
template_func = FULL_TEMPLATE if module_data["template"] == "全模板" else SIMPLE_TEMPLATE
page_func = module_data["page_func"].split("、") if module_data["page_func"] else []
full_func = list(set(page_func + template_func)) # 去重
full_func = [func.strip() for func in full_func if func.strip()] # 过滤空值
module_data["full_func"] = "、".join(full_func)
global_modules.append(module_data)
# 5. 格式化生成最终功能清单(匹配导出模板字段)
global_function_list = []
for module in global_modules:
func_count = len(module["full_func"].split("、")) if module["full_func"] else 0
remark_with_count = f"{module['remark']}(功能项数量:{func_count})" if module["remark"] else f"功能项数量:{func_count}"
global_function_list.append({
"页面功能": module["page_func"],
"功能项": module["full_func"],
关键细节:
• 功能项去重:避免页面功能与通用功能重复(如页面功能已含“查询”,模板再含“查询”时自动去重);
• 备注补充功能项数量:便于用户快速了解每个模块的功能覆盖度;
核心目标:下载当前预览的功能清单,严格匹配附件模板格式(合并标题行+灰色表头+数据行),支持用户自定义保存路径,提供下载反馈,确保下载的文档可直接使用,无需二次调整格式。
代码说明:先校验功能清单数据,再使用python-docx库创建Word文档,添加标题、生成日期、分隔线和标准化表格,填充功能清单数据,最后让用户选择保存路径,完成下载并给出反馈。
def export_function_list(self):
if not global_function_list:
if not self.generate_full_function():
# 2. 创建Word文档(python-docx核心API)
doc.add_heading(f"{global_system_name}功能清单", 0) # 0级标题(最大号)
doc.add_paragraph(f"生成日期:{datetime.now().strftime('%Y-%m-%d')}")
doc.add_paragraph("="*60) # 分隔线
# 4. 创建表格(3行4列:标题行+表头行+数据行)
table = doc.add_table(rows=3, cols=4)
table.style = "Table Grid" # 带边框样式
table.alignment = WD_TABLE_ALIGNMENT.CENTER # 表格居中
hdr_cells = table.rows[0].cells
hdr_cells[0].merge(hdr_cells[1]).merge(hdr_cells[2]).merge(hdr_cells[3])
hdr_cells[0].text = f"{global_system_name}功能清单"
shading_elem = parse_xml(r'<w:shd xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" w:fill="0070C0"/>')
hdr_cells[0]._tc.get_or_add_tcPr().append(shading_elem)
for run in hdr_cells[0].paragraphs[0].runs:
run.font.color.rgb = RGBColor(255, 255, 255)
headers = ["功能模块", "页面功能", "功能项", "备注"]
for i, header in enumerate(headers):
shading_elem = parse_xml(r'<w:shd xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" w:fill="BEBEBE"/>')
cell._tc.get_or_add_tcPr().append(shading_elem)
for run in cell.paragraphs[0].runs:
for paragraph in cell.paragraphs:
paragraph.alignment = WD_TABLE_ALIGNMENT.CENTER
for func in global_function_list:
row_cells = table.add_row().cells
row_cells[0].text = func["功能模块"]
row_cells[1].text = func["页面功能"] if func["页面功能"] else "无"
row_cells[2].text = func["功能项"]
row_cells[3].text = func["备注"] if func["备注"] else "无"
file_path = filedialog.asksaveasfilename(
defaultextension=".docx",
filetypes=[("Word文档", "*.docx"), ("所有文件", "*.*")],
initialfile=f"{global_system_name}_功能清单_{datetime.now().strftime('%Y%m%d')}"
self.func_log(f"📤 功能清单下载成功:{os.path.basename(file_path)}")
messagebox.showinfo("成功", f"功能清单已下载至:{file_path}")
下载功能关键设计:
• 格式精准匹配:严格按照附件模板的“合并标题行+灰色表头+数据行”结构;
• 自定义路径:支持用户选择保存位置,默认文件名含系统名称和日期,便于区分;
• 下载反馈:日志记录下载结果,弹窗提示保存路径,提升用户体验。
——未完待续——
下篇我们从测试用例模块讲起:知识库、生成、下载,还有AI批量改。
声明:本文为51Testing软件测试网 M&T. 用户投稿内容,该用户投稿时已经承诺独立承担涉及知识产权的相关法律责任,并且已经向51Testing承诺此文并无抄袭内容。发布本文的用途仅仅为学习交流,不做任何商用,未经授权请勿转载,否则作者和51Testing有权追究责任。如果您发现本公众号中有涉嫌抄袭的内容,欢迎发送邮件至:editor@51testing.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。