Search code examples
pythonpyqtpyqt5pytestpytest-qt

How to test a custom dialog window called using exec_()?


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'

Solution

  • 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"