首页 > 编程问答 >有什么方法可以将模拟对象伪装成 Qt 方法参数中可接受的 PyQt 对象吗?

有什么方法可以将模拟对象伪装成 Qt 方法参数中可接受的 PyQt 对象吗?

时间:2024-07-30 04:04:26浏览次数:7  
标签:python pyqt mocking pytest pytest-qt

这是 MRE。您需要 pytest 安装...事实上 pytest-qt 不会造成任何伤害。

import sys, pytest
from PyQt5 import QtWidgets
from unittest import mock 

class MyLineEdit(QtWidgets.QLineEdit):
    def __init__(self, parent):
        super().__init__(parent)
        self.setPlaceholderText('bubbles')

class Window(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Pytest MRE")
        self.setMinimumSize(400, 200)
        layout = QtWidgets.QVBoxLayout()
        qle = MyLineEdit(self)
        layout.addWidget(qle)
        self.setLayout(layout)

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

def test_MyLineEdit_sets_a_title():
    mock_window = mock.Mock(spec=QtWidgets.QWidget)

    print(f'mock_window.__class__ {mock_window.__class__}') # says "QWidget"
    print(f'type(mock_window) {type(mock_window)}') # says "mock.Mock"

    with mock.patch('PyQt5.QtWidgets.QLineEdit.setPlaceholderText') as mock_set_placeholder:
        # with mock.patch('PyQt5.QtWidgets.QLineEdit.__init__') as mock_super_init:
            my_line_edit = MyLineEdit(mock_window)

    mock_set_placeholder.assert_called_once()
    assert False

这说明了一个反复出现的问题,我不确定是否有解决方法:我想将模拟作为 PyQt 类的方法的参数传递(在本例中是构造函数:是将模拟传递给超类的行为 __init__ 会导致问题)。

通常,当我尝试运行这个测试,我得到:

E       TypeError: arguments did not match any overloaded call:
E         QLineEdit(parent: Optional[QWidget] = None): argument 1 has unexpected type 'Mock'
E         QLineEdit(contents: Optional[str], parent: Optional[QWidget] = None): argument 1 has unexpected type 'Mock'
test_grey_out_qle_exp.py:7: TypeError

我在很多场景中遇到了这个问题:“有意外的类型'Mock'”...

一遍又一遍,在我测试 PyQt 应用程序期间,我发现我必须传递 real QtWidget (或其他真实的 PyQt 类对象)。这反过来可能会导致各种各样的问题,最糟糕的是令人讨厌的间歇性“Windows 访问冲突”错误......取决于我试图从中获取结果的真实 PyQt 对象和模拟的组合类型。

我想要以某种方式在伪装下将模拟传递到 PyQt 代码中。我尝试过 MagicMock spec autospec 等的各种组合。但 PyQt 代码永远不会被愚弄。它似乎正在使用内置函数 type(x) 进行检查。但是,我不确定这一点,因为我无法检查任何源代码,因为 PyQt 模块似乎是从 C++ 代码自动生成的。

我想到的一件事:可能是可以修补内置函数 type() ...?可能是一个不太可能的解决方案,尤其是因为我怀疑 PyQt 模块在检查类型时没有使用 Python 代码。然而我确实对此进行了搜索,但无济于事。

PS,如注释掉的 mock.patch... 行所示,一种可能的解决方法是开始修补任何导致问题的 PyQt 超类方法...

稍后:musicamante 的建议

好主意。我尝试了这个:

class HybridMock(QtWidgets.QWidget, mock.Mock):
     def __init__(self, *args, **kwargs):
        # super().__init__(*args, **kwargs)
        QtWidgets.QWidget.__init__(self, *args, **kwargs)

def test_MyLineEdit_sets_a_title():
    # mock_window = mock.Mock(spec=QtWidgets.QWidget)
    mock_window = HybridMock()

运行时 pytest 这只是崩溃并说“Python已停止工作......”(没有任何解释)。 NB平台是W10。我并不感到惊讶。


你几乎找到了解决办法!将模拟与 QtWidgets.QWidget 混合的问题在于,它会触发真实的 QWidget 初始化,这在单元测试环境中不希望发生。

与其混合 mock.Mock ,不如使用 QtBot pytest-qt 包含的)来创建和测试你的 PyQt 应用程序:

import pytest

from PyQt5 import QtWidgets
from PyQt5.QtTest import QTest
from PyQt5.QtCore import Qt

@pytest.fixture
def window(qtbot):
    window = Window()
    qtbot.addWidget(window)  # 确保窗口被清理干净
    return window

def test_MyLineEdit_sets_a_title(window, qtbot):
    # 使用 qtbot 找到 MyLineEdit
    line_edit = window.findChild(MyLineEdit)
    assert line_edit is not None

    # 断言占位符文本
    assert line_edit.placeholderText() == "bubbles"

在这个例子中:

  1. qtbot fixture pytest-qt 提供的 qtbot fixture 让你可以与你的 PyQt 应用程序进行交互。

  2. window fixture :这个 fixture 创建了一个 Window 实例,并使用 qtbot.addWidget 将其添加到 Qt 的事件循环中。这确保了在测试结束后窗口被正确清理。

  3. 查找 MyLineEdit :在你的测试中,你使用 window.findChild(MyLineEdit) 来找到 MyLineEdit 实例。

  4. 断言 :现在你可以直接对 MyLineEdit 对象进行断言,例如检查它的占位符文本。

使用这种方法,你不需要模拟 QWidget ,从而避免了混合模拟和真实 PyQt 对象的复杂性。 qtbot 负责处理事件循环和窗口清理等任务,让你专注于测试应用程序的逻辑。

标签:python,pyqt,mocking,pytest,pytest-qt
From: 78808729

相关文章

  • 即使安装了软件包,也找不到 python 模块句子转换器
    对于我的python脚本(如下所示),我使用句子转换器包,其中包含SBERT模型。即使在执行“piplist”时明确列出该软件包,并且还安装了torch,所有内容都更新到最新版本,但仍然找不到该模块。脚本:importnumpyasnpimportpandasaspdfromsentence_transformersimportSenten......
  • 有没有办法在 python 中返回类实例的布尔属性?
    我想组织我玩的游戏中的汽车数据。为此,我创建了一个“Car”类。我希望将此类实例的一些属性作为布尔值,这样我就可以将此类的所有实例放入列表中并过滤它们。我当前的解决方案不起作用,因为,我收到以下错误消息:TypeError:__repr__returnednon-string(typeCar)我使用......
  • python 正则表达式匹配一行中的多个单词而不转到下一行
    我正在编写一个解析器来解析以下输出:admin@str-s6000-on-5:~$showinterfacestatusEthernet4InterfaceLanesSpeedMTUAliasVlanOperAdminTypeAsymPFC------------------------------------------......
  • 使用 Python 平滑和对称不规则形状和曲线
    我需要完成三项任务:正则化曲线曲线的对称性完成不完整的曲线例如,这里是输入和预期的输出图像:输入输出|||在一般设置中,形状可以由任何SVG曲线基元(贝塞尔曲线、直线、弧线)表示。为了统一表示,示例包含曲线的折线近似。这些折线保存为......
  • 在Python中通过绝对路径引用数据文件夹
    我有一个大型python项目,其中数据太大,无法每次都以相同的方式共享。不同的人可能会使用网络位置或将某些内容复制到本地驱动器。该路径由不同子文件夹中的脚本和笔记本使用。例如,我将创建一个config.py来定义数据文件夹的路径。importsyssys.path.append('../'......
  • python三天速成记(看完你就会)day3 满满干货~
    续上文啦~EXCEL表的操作上一篇文章讲了怎么读取和操作txt和csv文档,但其实我们生活中还有一个常用的文本格式那就是excel文件,特别是在对大量数据进行处理的时候。excel文件的用处和广泛。其实在python中有很多库可以处理excel文件,但是本文主要介绍使用最实用最广泛的库pan......
  • Python输入验证改进的其他方式
    题目[题目来源:Python编程快速上手——让繁琐工作自动化(第二版)第三章实践项目,下面的解答程序为我自己完成的,仅供参考。]编写一个名为collatz()的函数,他有一个名为number的参数。如果参数是偶数,那么collatz()就输出number//2,并返回该值。如果参数是奇数,那么collatz()就......
  • Python的PyInputPlus模块
    PyInputPlus模块简介PyInputPlus模块的功能:PyInputPlus模块是一个Python第三方模块,需要自己对它进行安装。包含与input()函数类似的、用于多种数据(如日期、数字、电子邮箱地址等)的函数。如果,用户输入了无效的内容,例如格式错误的日期或超过预期范围的数字,那么PyInputPlus模......
  • 用Python实现二进制搜索(二分查找)
    二进制搜索(binarysearch,又称二分搜索)是一种快速有效的搜索方法,用于搜索有序列表中的元素。importmathdefbinary_search(sorted_list,target):"""在有序列表sorted_list中查找目标值target的位置使用二分查找算法"""lower_bound=0#初始......
  • 超详细Python教程——使用Hexo搭建自己的博客
    使用Hexo搭建自己的博客对于一个程序员来说,搭建一个属于自己的博客平台是非常有意义的事情。首先,博客可以记录自己的成长历程,也是对自己一段时间学习和工作的总结和沉淀;其他,通过博客可以营销自己,增强自己在互联网或行业内的影响力,为将来更好的职业生涯打一个坚实的基础。前......