2

I am writing unit tests for a simple GUI written in PySide 1.2.2. I am working on Windows 7 and with Python 2.7.6.

I want to test this function, which gets activated when a button is clicked.

def setDestination(self):
    directory = QtGui.QFileDialog.getExistingDirectory(self, "Select Directory")
    self.destLineEdit.setText(directory)

So far I have come up with the following test case:

def test_browse_dest(self):
    # Reset the GUI to its defaults
    self.clear()

    # Click the destination browse button
    QTest.mouseClick(self.window.browseButton_2, QtCore.Qt.LeftButton)

    # Test paths
    destPath = os.path.join(os.getcwd(), TEST_DIR_B, "test")
    self.assertEqual(self.window.destLineEdit.text(), destPath)

This test does work, but it is interactive. I have to select the directory and click the Select Folder button. While this is certainly cool and fun to play with, I was wondering if there is a way to automate these actions.

I did try to just hide the file dialog and set the text in the line edit myself. First, I wrote this:

def setDestination(self):
    self.fileDialog = QtGui.QFileDialog()
    directory = self.fileDialog.getExistingDirectory(self, "Select Directory")
    self.destLineEdit.setText(directory)

Then I tried to access the file dialog inside the unit test.

def test_browse_dest(self):
    # Reset the GUI to its defaults
    self.clear()

    # Click the destination browse button
    QTest.mouseClick(self.window.browseButton_2, QtCore.Qt.LeftButton)
    self.window.fileDialog.hide()

    # Test paths
    destPath = os.path.join(os.getcwd(), TEST_DIR_B, "test")
    self.window.destLineEdit.setText(destPath)
    self.assertEqual(self.window.destLineEdit.text(), destPath)

However, that did not work. The file dialog still launched, and I had to interact with it to complete the test.

Christopher Spears
  • 1,105
  • 15
  • 32

2 Answers2

1

My solution was to build a test mode parameter into the GUI class that launches the file dialog. Something like this...

class MyWindow(QtGui.QMainWindow):
    def __init__(self, testMode=False):
        QtGui.QMainWindow.__init__(self)
        self.testMode = testMode

If testMode is true (when running a unit test for example), I just hide the dialog and then set the line edits manually in the unit tests.

This does work but I am worried if I am defeating the purpose of the tests. On the other hand, I guess you could say that I just "stubbed" a user's interaction.

Christopher Spears
  • 1,105
  • 15
  • 32
  • Thanks for this. As a suggestion, you could avoid cluttering the parameters by setting an attribute like `self._test_mode = False` and then patch it to be True when setting up your tests. Some other options are discussed here: https://stackoverflow.com/a/38860201 – Lorem Ipsum Jun 26 '22 at 04:13
0

Another option is to control the keyboard directly with keyboard module and delay input with Qt timer singleshoot. It's bit messy as means you cannot use your computer while the GUI is doing it's thing, but means you don't have to alter your base code:

import keyboard
QtCore.QTimer.singleShot(1, lambda: keyboard.write(base_dir + '/test_save_excel.xlsx'))
QtCore.QTimer.singleShot(10, lambda: keyboard.press('enter'))
# test method that calls dialog 
Joseph
  • 578
  • 3
  • 15