首页 > 解决方案 > QCoreApplication.postEvent(receiver, event) 仅使用输入功能才能提供良好的结果

问题描述

对代码的以下部分执行单一测试(我用...替换不必要的东西来简化)

from PyQt5.QtWidgets import QWidget, QLineEdit

// ...

class Controller(QWidget):
    def __init__(self, ...)
        // ...
        self.line_edit_name = QLineEdit()
        // ...
    def display_parameters(self, name, ...)
        self.line_edit_name.setText(name)
            self.line_edit_name.setReadOnly(False)
            self.line_edit_name.returnPressed.connect(
                partial(self.update_name, ...))
    def update_name(self, ...):
        // ...

我将这段代码用于使用 unittest 进行测试:

from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QCoreApplication, QEvent
// ...
a = Controller()
a.line_edit_name.setText("Shakespeare")
keyEvent = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Return, QtCore.Qt.NoModifier)
QCoreApplication.postEvent(a.line_edit_name, keyEvent)
// ...

它在调试阶段工作得非常好(我经常使用 input('foo') 来反省并逐步查看发生了什么)...但是如果我删除了输入函数,它就不再起作用了!!!!

总之:以下脚本给出了正确的结果

    a = Controller()
    a.line_edit_name.setText("Shakespeare")
    keyEvent = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Return, QtCore.Qt.NoModifier)
    QCoreApplication.postEvent(a.line_edit_name, keyEvent)
    input("foo")
    //...

以下不是

    a = Controller()
    a.line_edit_name.setText("Shakespeare")
    keyEvent = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Return, QtCore.Qt.NoModifier)
    QCoreApplication.postEvent(a.line_edit_name, keyEvent)
    // ...

因为单元测试是在持续集成期间自动完成的,所以我们不能使用输入函数。为什么这个结果以及没有输入功能怎么办?

编辑:通过进行各种测试,我认为 input() 函数只允许进程在下一个命令之前完成。我试过之后QCoreApplication.postEvent(a.line_edit_name, keyEvent),代替input("foo")threading.Event().wait(1)或者time.sleep(1)没有成功......

标签: pythonunit-testingpyqt5python-unittest

解决方案


在 Qt 中,发送事件和调用与信号相关的槽的任务是异步的,所以不要期望立即得到响应,而是必须等待。在这种情况下,您必须使用 QTest.qWait。

由于您不提供 MRE,因此我将仅展示一个简单的演示:

from functools import partial

from PyQt5.QtCore import QCoreApplication, QEvent, Qt
from PyQt5.QtGui import QKeyEvent
from PyQt5.QtWidgets import QApplication, QFormLayout, QLineEdit, QWidget
from PyQt5.QtTest import QTest


class Controller(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.current_name = ""
        self.line_edit_name = QLineEdit()
        lay = QFormLayout(self)
        lay.addRow("name", self.line_edit_name)

    def display_parameters(self, name):
        self.line_edit_name.setText(name)
        self.line_edit_name.setReadOnly(False)
        self.line_edit_name.returnPressed.connect(partial(self.update_name, name))

    def update_name(self, name):
        self.current_name = name


def test_controller():
    app = QApplication([])

    a = Controller()
    a.display_parameters("Foo")

    assert a.current_name != "Foo"

    keyEvent = QKeyEvent(QEvent.KeyPress, Qt.Key_Return, Qt.NoModifier)
    QCoreApplication.postEvent(a.line_edit_name, keyEvent)
    # QTest.keyClick(a.line_edit_name, Qt.Key_Return)

    QTest.qWait(1000)

    assert a.current_name == "Foo"


test_controller()

另一方面,如果你想做测试,那么最好使用 QtTest 子模块,它已经有几个简化的功能,除了你可以使用 pytest-qt。


推荐阅读