Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unittest Tkinter File Dialog

Question

Is there any way to automate a tkFileDialog selection to run it through unittest? The following is the only use of tkinter in my application:

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

Edit: I didn't mention that this part of the code was trapped in a method call from a class outside my control.


Background

I've built a local app that creates an http server on localhost and runs its GUI with HTML/CSS/JS in a web browser. Because of browser restrictions, I can't use the built-in file dialog and so have to send this request through Python. I want this to run on a OSX with the built-in Python 2.5. I'm not very familiar with Tcl/Tk.

Attempt #1

If I could get to the underlying widgets, I could generate the clicks like in this question. However, looking at the dialog source, it appears to me that the Tcl call in lines 48-50 is blocking. Is this a correct assumption?

Attempt #2

I thought there might be a way using Tcl commands directly through root.tk.call. Since I'm on Python2, I think the underlying Tcl is a single call to tk_getOpenFile. Would I have to ensure the Tcl interpreter is threaded? Is there any Tcl/Tk command that can help me out here?

Attempt #3

I could implement the file selection from scratch using os.listdir etc. (Probably in a separate HTML page communicating back and forth with the server). It would be more than a little painful and hopefully avoidable.


Solution

Based on A. Rodas's answer below, I came up with the following:

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
like image 530
Felipe Avatar asked Jun 14 '13 17:06

Felipe


2 Answers

Unit testing of Tkinter code is not an easy issue. For instance, the IDLE does not have a proper test suite, even though it is part of the standard library. Since you mention that this is going to be the only use of Tkinter in your application, I'd suggest to make unit tests for the outcome of this code: the value of filename.

For instance, you can have a test for a .csv file, and another one for an incorrect file extension. Since tkFileDialog returns an empty string if it is closed by the user, add also a test where 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'
        # ...
like image 114
A. Rodas Avatar answered Nov 11 '22 12:11

A. Rodas


You could just patch the calls to tkinter: patch tk.Tk() because most CI's will error because they don't have displays. Also patch the file dialog so you can simulate return values and that it is being called with what you expect.

@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)
like image 26
toonarmycaptain Avatar answered Nov 11 '22 11:11

toonarmycaptain