使用pytest-qt的QtBot.waitSignal()测试QDialog失败问题排查
问题:使用pytest-qt的waitSignal测试QDialog时超时失败
我在用pytest-qt的waitSignal()方法测试QDialog时遇到了超时问题,以下是最小复现代码:
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()
运行测试后返回超时错误:
$ 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 =============================================================================
改用waitUntil()方法则可以正常运行:
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
问题原因与解决方法
问题出在调用顺序错误:你先触发了按钮点击(此时dialog.finished信号已经发出),之后才进入waitSignal()上下文开始监听信号——信号早已发送完毕,waitSignal()自然捕获不到,最终超时。
waitSignal()的正确用法是先启动信号监听,再执行触发信号的操作。修正后的测试代码如下:
def test_dialog(qtbot, qapp): app = qapp dialog, button1 = get_dialog(app) # 先进入waitSignal上下文监听信号,再触发按钮点击 with qtbot.waitSignal(dialog.finished, timeout=2000): button1.click() assert True
这样修改后,waitSignal()会先注册信号监听器,再执行按钮点击操作触发dialog.finished信号,就能正常捕获到信号,不会出现超时错误。
内容的提问来源于stack exchange,提问作者Håkon Hægland
相关产品推荐
相关产品推荐

