pythonpyqtpytestpytest-qt

How to use QtBot.waitSignal() in pytest-qt?


I am using pytest-qt and its waitSignal() method to test a QDialog but it is not working. Here is a minimal example (my real application is more complex):

import sys
from PyQt6.QtWidgets import (
    QApplication, QDialog, QGridLayout, QPushButton
)

def get_dialog(app):
    dialog = QDialog()
    dialog.resize(300,200)
    dialog.setWindowTitle("Test dialog")
    layout = QGridLayout()
    button1 = QPushButton("&Ok", dialog)
    def ok_pressed():
         dialog.done(0)
    button1.clicked.connect(ok_pressed)
    layout.addWidget(button1, 0, 0)
    button2 = QPushButton("&Cancel", dialog)
    def cancel_pressed():
         dialog.done(1)
    button2.clicked.connect(cancel_pressed)
    layout.addWidget(button2, 0, 1)
    dialog.setLayout(layout)
    dialog.open()
    return dialog, button1

def main():
    app = QApplication(sys.argv)
    get_dialog(app)
    app.exec()

def test_dialog(qtbot, qapp):
    app = qapp
    dialog, button1 = get_dialog(app)
    button1.click()
    with qtbot.waitSignal(dialog.finished, timeout=2000):
        pass
    assert True

if __name__ == '__main__':
    main()

Running this example with pytest gives:

$ pytest t.py
============================================================================ test session starts ============================================================================
platform linux -- Python 3.10.4, pytest-7.3.2, pluggy-1.0.0
PyQt6 6.5.1 -- Qt runtime 6.5.0 -- Qt compiled 6.5.1
rootdir: /home/hakon/test/python/pytest-qt/qdialog-cancel/pre
plugins: mock-3.11.1, qt-4.2.0, anyio-3.7.1
collected 1 item                                                                                                                                                            

t.py F                                                                                                                                                                [100%]

================================================================================= FAILURES ==================================================================================
________________________________________________________________________________ test_dialog ________________________________________________________________________________

qtbot = <pytestqt.qtbot.QtBot object at 0x7fea78d47a30>, qapp = <PyQt6.QtWidgets.QApplication object at 0x7fea78d5c1f0>

    def test_dialog(qtbot, qapp):
        app = qapp
        dialog, button1 = get_dialog(app)
        button1.click()
>       with qtbot.waitSignal(dialog.finished, timeout=2000):
E       pytestqt.exceptions.TimeoutError: Signal finished(int) not emitted after 2000 ms

t.py:36: TimeoutError
--------------------------------------------------------------------------- Captured Qt messages ----------------------------------------------------------------------------
QtWarningMsg: Could not connect "org.freedesktop.IBus" to globalEngineChanged(QString)
========================================================================== short test summary info ==========================================================================
FAILED t.py::test_dialog - pytestqt.exceptions.TimeoutError: Signal finished(int) not emitted after 2000 ms
============================================================================= 1 failed in 2.02s =============================================================================

so the signal dialog.finished is not emitted or I am not using waitSignal() correctly. However, if I use waitUntil() instead of waitSignal() it works fine:

def test_dialog(qtbot, qapp):
    app = qapp
    dialog, button1 = get_dialog(app)
    dialog_done = False
    def dialog_done_cb():
        nonlocal dialog_done
        dialog_done = True
    dialog.finished.connect(dialog_done_cb)
    button1.click()
    qtbot.waitUntil(lambda: dialog_done)
    assert True

Since this works, it also indicates that the dialog.finished signal is emitted and I am not using waitSignal() correctly. Any idea what might be the problem?


Solution

  • I am not using waitSignal() correctly.

    It seems to work if I move button1.click() inside the context manager:

    def test_dialog(qtbot, qapp):
        app = qapp
        dialog, button1 = get_dialog(app)
        with qtbot.waitSignal(dialog.finished, timeout=2000):
            button1.click()
        assert True
    

    Maybe it did not work because the signal was emitted too early?