如何使用pytest对Tkinter开发的问答小游戏应用进行功能测试
Tkinter 随机答复应用 pytest 测试方案
一、原代码前置修正
先修正原代码中的语法/逻辑错误,保证功能可正常运行:
- 补全缺失的导入语句:
import random, time - 替换代码中未定义的
frame_question_answer_with_scroll、frame_answer_with_scroll为已经创建的frame_question_answer、frame_answer - 可选择将全局组件封装到类中,降低函数对全局变量的依赖,更便于后续测试。
二、测试用例编写思路
你当前的测试代码存在两个核心问题:
questionQuery是按钮点击事件的回调函数,第一个参数是事件对象,不是传入的问题字符串,问题内容实际是从全局的text_box_question组件读取的- 两个功能函数都没有返回值,所有结果都输出到GUI的文本框组件中,不能通过判断返回值测试,需要读取文本框的内容做断言。
环境准备
不需要额外GUI测试库,直接用标准库tkinter和pytest即可,若要简化时间相关的模拟逻辑,可以安装pytest-mock做函数mock:
pip install pytest pytest-mock
测试代码示例
import tkinter as tk import pytest from magicBallProg import questionQuery, answerQuery, RESPONSES # 定义通用fixture,每个测试用例自动创建和销毁Tk实例,默认隐藏窗口避免测试时弹窗干扰 @pytest.fixture(autouse=True) def tk_app(monkeypatch, mocker): # 跳过所有sleep等待,加快测试速度 mocker.patch('time.sleep') # 重写tk的after方法,直接执行回调不做延迟等待 def mock_after(self, delay, func, *args): func() monkeypatch.setattr(tk.Tk, 'after', mock_after) # 初始化隐藏的Tk窗口 window = tk.Tk() window.withdraw() # 替换原业务代码的全局window变量 import magicBallProg magicBallProg.window = window # 按照原业务代码逻辑初始化所有组件 frame_question_answer = tk.Frame( master=window, relief=tk.RIDGE, borderwidth=5 ) frame_answer = tk.Frame( master=window, relief=tk.RIDGE, borderwidth=5 ) text_box_question = tk.Text( master=frame_question_answer, width=67, height=5, bg='#EBF1F2', fg='black' ) text_box_answer = tk.Text( master = frame_answer, width=48, height=7.5, bg='#EBF1F2', fg='black' ) button_ask = tk.Button( master=frame_question_answer, text='Ask', width=15, height=2, bg='#B533FF', fg='yellow', activebackground='#C4B1C3' ) # 替换原业务代码的全局组件变量 magicBallProg.frame_question_answer = frame_question_answer magicBallProg.frame_answer = frame_answer magicBallProg.text_box_question = text_box_question magicBallProg.text_box_answer = text_box_answer magicBallProg.button_ask = button_ask # 组件布局和原代码保持一致 frame_question_answer.grid(row=0, column=0, columnspan=1, sticky='e') frame_answer.grid(row=0, column=1, columnspan = 1, sticky='w') text_box_question.grid(row=0, column=0, sticky='news') text_box_answer.grid(row=0, column=0, sticky='news') button_ask.grid(row=3, column=0, sticky='ew') yield window, text_box_question, text_box_answer # 测试结束销毁窗口释放资源 window.destroy() # 测试空输入场景下的questionQuery逻辑 def test_questionQuery_empty_input(tk_app): window, text_q, text_a = tk_app # 问题框留空 text_q.insert('1.0', '') # 调用事件回调,函数内未用到event参数传None即可 questionQuery(None) # 读取答复框内容做断言 answer = text_a.get('1.0', tk.END).strip() assert answer == 'Ask your question' # 测试有输入场景下的questionQuery逻辑 def test_questionQuery_has_input(tk_app, mocker): window, text_q, text_a = tk_app # mock answerQuery函数,仅验证是否被调用 mock_answer = mocker.patch('magicBallProg.answerQuery') # 输入测试问题 text_q.insert('1.0', 'Am I right?') questionQuery(None) # 验证answerQuery被正常调用 mock_answer.assert_called_once() # 测试answerQuery的输出逻辑 def test_answerQuery(tk_app): window, text_q, text_a = tk_app answerQuery() # 读取所有输出内容按行拆分 output = text_a.get('1.0', tk.END).strip().split('\n') # 验证中间提示文本符合预期 assert output[0] == 'Hmm...' assert output[1] == 'Looking forward to learn about the future' # 验证最终随机结果在预设的答复列表内 assert output[2] in RESPONSES
三、运行测试
在项目根目录执行以下命令即可查看测试结果:
pytest 你的测试文件名.py -v
内容的提问来源于stack exchange,提问作者Alex_Kazantsev
相关产品推荐
相关产品推荐

