1

I'm trying to write a system test for my project. I have a controller class which launches the various windows. However, I can't seem to control windows launch using exec with the qtbot.

Here is an MVCE:

from PyQt5.QtWidgets import *
from PyQt5 import QtGui
class Controller:
    def __init__(self):
        self.name = None
        self.a = WindowA(self)

    def launchB(self):
        self.b = WindowB(self)

        if self.b.exec_():
            self.name = self.b.getData()

class WindowA(QDialog):
    def __init__(self, controller):
        super(WindowA, self).__init__()
        self.controller = controller
        layout = QVBoxLayout()
        self.button = QPushButton('Launch B')
        self.button.clicked.connect(self.controller.launchB)
        layout.addWidget(self.button)
        self.setLayout(layout)
        self.show()

class WindowB(QDialog):
    def __init__(self, controller):
        super(WindowB, self).__init__()
        self.controller = controller
        layout = QVBoxLayout()
        self.le = QLineEdit()
        self.button = QPushButton('Save')
        self.button.clicked.connect(self.save)
        layout.addWidget(self.le)
        layout.addWidget(self.button)
        self.setLayout(layout)
        self.show()

    def getData(self):
        return self.le.text()

    def save(self):
        if self.le.text():
            self.accept()
            self.close()   
        else:
            self.reject()

from PyQt5.QtWidgets import QApplication

if __name__ == '__main__':

    import sys

    app = QApplication(sys.argv)
    window = Controller()
    sys.exit(app.exec_())

I'd like to test that the user successfully enters data in the lineedit. In my test I'm able to successfully click the button in WindowA to launch WindowB, but am unable to use keyClicks to enter data in the lineedit.

Here is the test:

def test_1(qtbot):
    control = Controller()
    qtbot.mouseClick(control.a.button, QtCore.Qt.LeftButton)

    qtbot.keyClicks(control.b.le, 'Test_Project')
    qtbot.mouseClick(control.b.button, QtCore.Qt.LeftButton)

    assert control.name == 'Test_Project'
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Evan Brittain
  • 547
  • 5
  • 15

1 Answers1

4

The problem is that using exec_() blocks all synchronous tasks until the window is closed, the solution is to use a QTimer to launch the remaining tasks asynchronously:

def test_1(qtbot):
    control = Controller()

    def on_timeout():
        qtbot.keyClicks(control.b.le, "Test_Project")
        qtbot.mouseClick(control.b.button, QtCore.Qt.LeftButton)

    QtCore.QTimer.singleShot(0, on_timeout)
    qtbot.mouseClick(control.a.button, QtCore.Qt.LeftButton)

    assert control.name == "Test_Project"
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Inspired by this answer I've used this sort of technique once or twice, but I think it has the unfortunate consequence occasionally of causing test leakage, and errors occurring during teardown or setup (of the next test). At the least I suggest you need to put `QtCore.QCoreApplication.processEvents()` before the final `assert`. There is also `qtbot.waitUntil()`, which is probably safer than `QTimer.singleShot()`... – mike rodent Dec 26 '21 at 12:09