0

在 的子类中QFileDialog有一个方法 ,on_dir_entered应该在QFileDialog' 的信号directoryEntered触发时调用它,因此:

self.directoryEntered.connect(self.on_dir_entered)

问题是信号需要很长时间才能生效。最初,我受到著名 PyQt5 专家 eyllanesc 的回答的启发。通过一个孤立的测试,这种技术QTimer.singleShot()可以使用,尽管我从一开始就对它有模糊的怀疑。事实上,在我的机器上,我发现这种事情“测试泄漏”,特别是当有不止一种这样的测试方法时:奇怪的错误显然发生在测试本身之外:

TEARDOWN ERROR: Exceptions caught in Qt event loop:

...所以我回到 pytest-qt 文档,发现有各种可用的方法开始wait...似乎可以解决信号或其他事件需要花费不可忽略的时间才能生效的问题。所以我做了一些尝试来测试信号directoryEntered

def test_directoryEntered_triggers_on_dir_entered(request, qtbot, tmpdir):
    project = mock.Mock()
    project.main_window = QtWidgets.QWidget()
    project.home_dir_path = pathlib.Path(str(tmpdir))
    fd = save_project_dialog_class.SaveProjectDialog(project)
    with mock.patch.object(fd, 'on_dir_entered') as mock_entered:
        fd.directoryEntered.emit('dummy')
    qtbot.waitSignal(fd.directoryEntered, timeout=1000)
    mock_entered.assert_called_once()

接着

def test_directoryEntered_triggers_on_dir_entered(qtbot, tmpdir):
    project = mock.Mock()
    project.main_window = QtWidgets.QWidget()
    project.home_dir_path = pathlib.Path(str(tmpdir))
    fd = SaveProjectDialog(project)
    qtbot.waitSignal(fd.directoryEntered, timeout=1000)
    with mock.patch.object(fd, 'on_dir_entered') as mock_entered:
        fd.directoryEntered.emit('dummy')
    mock_entered.assert_called_once()

接着

def test_directoryEntered_triggers_on_dir_entered(qtbot, tmpdir):
    project = mock.Mock()
    project.main_window = QtWidgets.QWidget()
    project.home_dir_path = pathlib.Path(str(tmpdir))
    fd = SaveProjectDialog(project)
    with mock.patch.object(fd, 'on_dir_entered') as mock_entered:
        fd.directoryEntered.emit('dummy')
    qtbot.wait(1000)
    mock_entered.assert_called_once()

全部失败:该方法被调用 0 次。

我也试过:

def test_directoryEntered_triggers_on_dir_entered(qtbot, tmpdir):
    project = mock.Mock()
    project.main_window = QtWidgets.QWidget()
    project.home_dir_path = pathlib.Path(str(tmpdir))
    fd = SaveProjectDialog(project)
    with mock.patch.object(fd, 'on_dir_entered') as mock_entered:
            fd.directoryEntered.emit('dummy')
    def check_called():
        mock_entered.assert_called_once()
    qtbot.waitUntil(check_called)

... 这超时(默认 5000 毫秒)。我已经反复检查并检查了connect在此信号上设置的代码是否已执行。

之后

print通过在被调用的 slot ( ) 中添加一条语句,on_dir_entered我现在看到了问题所在:尽管有这with mock.patch...条线,但该方法并没有被嘲笑!

在我对嘲笑等知识的低水平上,我倾向于认为这是因为使用信号emit()来触发事件的事实:我想不出另一种解释。

注意这个信号是由 a 中的一个或两个事件“自然地”触发的QFileDialog(例如单击“转到父目录” QToolButton)。也许你必须这样做......所以我尝试了这个:

def test_directoryEntered_triggers_on_dir_entered(request, qtbot, tmpdir):
    project = mock.Mock()
    project.main_window = QtWidgets.QWidget()
    project.home_dir_path = pathlib.Path(str(tmpdir))
    fd = save_project_dialog_class.SaveProjectDialog(project)
    to_parent_button = fd.findChild(QtWidgets.QToolButton, 'toParentButton')
    print(f'qtbot {qtbot} type(qtbot) {type(qtbot)}')
    with mock.patch.object(SaveProjectDialog, 'on_dir_entered') as mock_entered:
        qtbot.mouseClick(to_parent_button, QtCore.Qt.LeftButton)
    def check_called():
        mock_entered.assert_called_once()
    qtbot.waitUntil(check_called, timeout=1000)

暂停。0 次通话。我再次能够确定正在调用真正的方法,并且补丁不起作用。

我做错了什么,有没有办法用 pytest-qt 的东西来测试它?

4

1 回答 1

0

我想我可能已经找到了答案。pytest-qt 方面的专家不妨评论一下。

我想我发现(这可能很明显)上述尝试使用的问题qtbot.waitUntil()是,一旦执行离开上下文管理器块,我设置的补丁就不再适用,并且对方法的调用确实是,延迟,这是重点。

因此,整体测试方法装饰器补丁是一种方法(或者在设置补丁上下文管理器后保留缩进......)。我发现以下两项都通过了,而且都不是误报(即,如果我注释掉设置 的应用程序代码行,它们就会失败connect):

@mock.patch('SaveProjectDialog.on_dir_entered')
def test_directoryEntered_signal_triggers_on_dir_entered(mock_entered, request, qtbot, tmpdir):
    project = mock.Mock()
    project.main_window = QtWidgets.QWidget()
    project.home_dir_path = pathlib.Path(str(tmpdir))
    fd = SaveProjectDialog(project)
    fd.directoryEntered.emit('dummy')
    def check_called():
        mock_entered.assert_called_once()
    qtbot.waitUntil(check_called, timeout=1000)
    
@mock.patch('SaveProjectDialog.on_dir_entered')
def test_on_click_toParentButton_triggers_on_dir_entered(mock_entered, request, qtbot, tmpdir):
    project = mock.Mock()
    project.main_window = QtWidgets.QWidget()
    project.home_dir_path = pathlib.Path(str(tmpdir))
    fd = SaveProjectDialog(project)
    to_parent_button = fd.findChild(QtWidgets.QToolButton, 'toParentButton')
    qtbot.mouseClick(to_parent_button, QtCore.Qt.LeftButton)
    def check_called():
        mock_entered.assert_called_once()
    qtbot.waitUntil(check_called, timeout=1000)

更新
原来 usingqtbot.waitUntil可能不是必需的,这样的测试通常可以通过 usingQCoreApplication.processEvents()。因此上面的第二个测试将被重写:

@mock.patch('SaveProjectDialog.on_dir_entered')
def test_on_click_toParentButton_triggers_on_dir_entered(mock_entered, request, qtbot, tmpdir):
    project = mock.Mock()
    project.main_window = QtWidgets.QWidget()
    project.home_dir_path = pathlib.Path(str(tmpdir))
    fd = SaveProjectDialog(project)
    to_parent_button = fd.findChild(QtWidgets.QToolButton, 'toParentButton')
    qtbot.mouseClick(to_parent_button, QtCore.Qt.LeftButton)
    QtCore.QCoreApplication.processEvents()
    mock_entered.assert_called_once()
于 2021-11-17T17:42:54.623 回答