自动化 tkinter 文件对话以进行功能测试

问题描述

我有一个 Python tkinter 脚本,我想对其运行一些功能测试。该脚本包括 rgbArraytkinter.filedialog.askopenfilename(),所以我想要上传/下载文件的测试的一部分。我尝试使用 tkinter.filedialog.asksaveasfilename() 来尝试自动点击鼠标并发送按键来尝试自动执行这些操作,但这样做没有用(屏幕上没有任何可见的变化,也没有加载任何文件)。

尝试使用 pyautogui 进行功能测试

pyautogui

脚本

class TestOrganizeAttendance(unittest.TestCase):
    def setUp(self):
        self.organizer = AttendanceOrganizer()
    ...

    def test_attendance_organizer_window_operation(self):
        ...
        #User clicks button and their computer's files appear
        self.organizer.upload_file_button.invoke()
        self.assertIn(
            "explorer.exe",[p.name() for p in psutil.process_iter()])

        #User selects a file to be uploaded
        filepath = os.path.abspath(
            os.path.join('.','tests','sample_attendance.csv'))
        pyautogui.PAUSE = 2.5
        pyautogui.hotkey('alt','d')
        pyautogui.typewrite(filepath)
        pyautogui.hotkey('enter')
        ....

解决方法

上述functional_tests.py代码的问题在于tkinter.filedialog.askopenfilenametkinter.filedialog.asksaveaskfilename都是阻塞函数,即函数必须在执行进一步代码之前完成。避免此问题的最简单方法是使用 threading 创建一个线程,该线程执行与主线程分开的所有 pyautogui 函数。

functional_tests.py 和 pyautogui

class TestOrganizeAttendance(unittest.TestCase):

    def setUp(self):
        ...

    def tearDown(self):
        ...

    def automate_file_dialogue(self,filepath):
        while "explorer.exe" not in [p.name() for p in psutil.process_iter()]:    #Wait for the file dialogue to open
            continue
        directory,filename = os.path.split(filepath)
        pyautogui.hotkey('alt','d')    #Keyboard shortcut to focus the address bar
        pyautogui.typewrite(directory)
        pyautogui.hotkey('enter')
        pyautogui.hotkey('tab')
        pyautogui.hotkey('tab')
        pyautogui.hotkey('tab')    #Repeatedly press TAB to remove focus from the address bar
        pyautogui.hotkey('alt','n')    #Keyboard shortcut to focus the file name
        pyautogui.hotkey(filename)
        pyautogui.hotkey('enter')

    def test_attendance_organizer_window_operation(self):
        ...
        filepath = os.path.abspath(
            os.path.join('tests','sample_attendance.csv'))
        upload_thread = threading.Thread(
            target=self.automate_file_dialogue,args=(filepath,))
        upload_thread.start()
        upload_thread.start()
        self.assertTrue(os.path.exists(filepath))
        self.organizer.upload_file_button.invoke()
        self.assertIn(
            "explorer.exe",[p.name() for p in psutil.process_iter()])
        upload_thread.join()
        ...
        filepath = os.path.abspath(
            os.path.join('tests','new_sample_attendance'))
        download_thread = threading.Thread(
            target=self.automate_file_dialogue,))
        download_thread.start()
        self.assertFalse(os.path.exists(filepath))
        self.organizer.download_file_button.invoke()
        self.assertIn(
            "explorer.exe",[p.name() for p in psutil.process_iter()])
        download_thread.join()