单元测试 Tkinter 文件对话框

8

问题

有没有办法自动化 tkFileDialog 的选择并运行它通过 unittest?以下是我应用程序中唯一使用 tkinter 的方式:

root = Tkinter.Tk()
types = [('Comma Separated Values', '.csv'), ('All Files', '*')]
filename = tkFileDialog.askopenfilename(parent=root,
                                        filetypes=types)
root.destroy()

编辑:我没有提到这部分代码被困在了一个我无法控制的类的方法调用中。

背景

我建立了一个本地应用程序,在localhost上创建了一个HTTP服务器,并在Web浏览器中使用HTML/CSS/JS运行其GUI。由于浏览器限制,我无法使用内置文件对话框,因此必须通过Python发送此请求。我希望这能在内置Python 2.5的OSX上运行。我不太熟悉Tcl/Tk

尝试 #1

如果我可以访问底层小部件,我就可以像这个问题中那样生成点击事件。但是,查看对话框源代码,我认为第48-50行的Tcl调用是阻塞的。这是正确的假设吗?

尝试 #2

我想可能有一种方法可以直接通过root.tk.call使用Tcl命令。由于我使用的是Python2,我认为底层的Tcl是对tk_getOpenFile的单个调用。我是否需要确保Tcl解释器是线程化的?这里有任何Tcl/Tk命令可以帮助我吗?

尝试 #3

我可以使用os.listdir等从头开始实现文件选择(可能在与服务器来回通信的单独HTML页面中)。这将是非常痛苦的,但希望可以避免。


解决方案

基于A. Rodas下面的答案,我想出了以下解决方案:

import tkFileDialog
old_dialog = tkFileDialog.askopenfilename
try:
    tkFileDialog.askopenfilename = lambda *args, **kw: filename

    # First test dialog cancelled
    filename = ''
    method_that_calls_tk()
    # run some assertions

    # Next test a valid file name with valid contents
    filename = self.VALID_FILENAME
    method_that_calls_tk()
    # run some assertions

    # Now test a valid file name with invalid contents
    filename = self.INVALID_CONTENTS_FILENAME
    method_that_calls_tk()
    # run some assertions

    # Now test an invalid file name
    filename = self.INVALID_FILENAME
    method_that_calls_tk()
    # run some assertions
finally:
    tkFileDialog.askopenfilename = old_dialog

这是哪个平台?在X11上注入事件有一定的可能性,但在其他平台(即使用真正的本地对话框的平台)上,这非常困难。更简单的方法是完全模拟 tkFileDialog... - Donal Fellows
这将在X11上进行。最终我模拟了askopenfile函数。顺便问一下,您有没有链接或资源可以让我了解如何在X11上注入事件? - Felipe
2个回答

4
在Tkinter代码中进行单元测试并不是一件容易的事情。例如,即使它是标准库的一部分,但IDLE没有适当的测试套件。既然您提到这将是应用程序中唯一使用Tkinter的地方,我建议为此代码的结果(即filename值)编写单元测试。
例如,您可以针对.csv文件进行一个测试,另外一个测试则是错误的文件扩展名。由于tkFileDialog如果被用户关闭,则返回空字符串,因此还要添加一个测试,其中filename =''
import unittest

class TestFileDialog(unittest.TestCase):

    def test_dialog_closed(self):
        filename = ''
        # ...

    def test_incorrect_extension(self):
        filename = '/path/to/another/filetype'
        # ...

    def test_csv_extension(self):
        filename = '/path/to/correct/file.csv'
        # ...

这帮助我想出了一个更简单、更有效的解决方案。我已经将其作为我的问题编辑,以防有人偶然发现。谢谢! - Felipe

1
您可以仅修补对tkinter的调用: 修补tk.Tk(),因为大多数CI会出错,因为它们没有显示器。 还需要修补文件对话框,以便您可以模拟返回值,并确保它被调用时符合您的预期。
@patch('your_module.tk.Tk')
def test_your_stuff(self, Tk)

    with @patch('your_module.tkFileDialog.askopenfilename') as file_dialog:
        expected = 'expected return value'
        assert expected = your_function_calling_file_dialog()

        file_dialog.assert_called_once_with(whatever, you, expect, it, to, be, called, with)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接