简体   繁体   English

如何检查对信号的响应

[英]How to check response to a signal

In a subclass of QFileDialog there is a method, on_dir_entered , which should be called when the QFileDialog 's signal directoryEntered fires, thus:QFileDialog的子类中有一个方法on_dir_entered ,当QFileDialog的信号directoryEntered触发时应该调用它,因此:

self.directoryEntered.connect(self.on_dir_entered)

The problem is that a signal takes a non-negligible time to take effect.问题是信号需要很长时间才能生效。 Originally I was inspired by this answer by eyllanesc, a notable PyQt5 expert.最初,我受到了著名 PyQt5 专家 eyllanesc 的回答的启发。 With an isolated test this sort of technique using QTimer.singleShot() can work, although I had vague doubts about it from the beginning.通过一个独立的测试,这种使用QTimer.singleShot()的技术可以工作,尽管我从一开始就对它有模糊的怀疑。 And indeed, it turns out on my machine that I get "test leakage" with this sort of thing, particularly when there is more than one such test method: strange errors apparently occurring outside the tests themselves:事实上,在我的机器上,我发现这种事情是“测试泄漏”,特别是当有不止一种这样的测试方法时:奇怪的错误显然发生在测试本身之外:

TEARDOWN ERROR: Exceptions caught in Qt event loop:

... so I went back to the pytest-qt docs and found that there are various methods available beginning wait... seemingly to cater to the problem of signals or other events taking a non-negligible time to have effect. ...所以我回到 pytest-qt 文档,发现有各种可用的方法开始wait...似乎是为了迎合信号或其他事件需要花费不可忽略的时间才能生效的问题。 So I made a few tries to test signal directoryEntered :所以我做了一些尝试来测试信号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()

and then接着

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()

and then接着

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()

All fail: the method is called 0 times.全部失败:该方法被调用 0 次。

I also tried:我也试过:

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)

... this times out (default 5000 ms). ... 这超时(默认 5000 毫秒)。 I have double-checked and triple-checked that the code setting up connect on this signal is executed.我已经反复检查并检查了是否执行了在此信号上设置connect的代码。

Later之后

By putting a print statement in the called slot ( on_dir_entered ) I now see what the problem is: despite the with mock.patch... line, the method is not being mocked!通过在被调用的插槽( on_dir_entered )中放置一个print语句,我现在看到了问题所在:尽管使用了with mock.patch...行,但该方法并未被模拟!

At my low level of knowledge of mocking etc. I am tending to assume that this is because of the fact of using a signal with emit() to trigger the event: I can't think of another explanation.在我对 mocking 等的知识水平较低的情况下。我倾向于认为这是因为使用带有emit()的信号来触发事件:我想不出另一种解释。

NB this signal is fired "naturally" by one or two events in a QFileDialog (such as clicking the "go to parent directory" QToolButton ).注意这个信号是由QFileDialog中的一个或两个事件“自然”触发的(例如单击“转到父目录” QToolButton )。 Maybe you have to do it that way... So I tried this:也许你必须这样做......所以我尝试了这个:

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)

Time out.暂停。 0 calls. 0 次通话。 Again I was able to ascertain that the real method is being called, and the patch is not working.我再次能够确定正在调用真正的方法,并且补丁不起作用。

What am I doing wrong and is there a way to test this with something from pytest-qt?我做错了什么,有没有办法用 pytest-qt 的东西来测试它?

I think I may have found the answer.我想我可能已经找到了答案。 Experts in pytest-qt may like to comment. pytest-qt 方面的专家不妨评论一下。

I think I worked out (this is probably very obvious) that the problem in the above attempts using qtbot.waitUntil() was that the patch I had set up no longer applied once the execution left the context manager block, and the call to the method was, indeed, delayed, which was the whole point.我想我发现(这可能很明显)上述尝试使用qtbot.waitUntil()的问题是,一旦执行离开上下文管理器块,我设置的补丁就不再适用,并且调用确实,方法被延迟了,这就是重点。

A whole-test-method decorator patch is therefore one approach (or else retain the indent after setting up the patch context manager...).因此,整体测试方法装饰器补丁是一种方法(或者在设置补丁上下文管理器后保留缩进......)。 I found that both the following passed, and neither was a false positive (ie they failed if I commented out the app code line setting up the connect ):我发现以下两项都通过了,而且都不是误报(即,如果我注释掉设置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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM