python - Pytest 模拟补丁 - 如何排除故障?

标签 python mocking pytest pyarrow

我遇到了一个我认为是使用模拟补丁的常见问题,因为我无法找出正确的东西来补丁。

我有两个问题希望得到帮助。

  1. 关于如何解决以下示例中的特定问题的想法
  2. 可能最重要的是关于如何最好地解决“我应该修补哪个”问题的专业提示/指示/想法/建议。我遇到的问题是,如果不完全了解补丁的工作原理,我什至不知道我应该寻找什么,结果发现自己在玩猜谜游戏。

一个使用 pyarrow 的例子目前让我很痛苦:

我的模块.py

import pyarrow

class HdfsSearch:
    def __init__(self):
        self.fs = self._connect()

    def _connect(self) -> object:
        return pyarrow.hdfs.connect(driver="libhdfs")

    def search(self, path: str):
        return self.fs.ls(path=path)

测试模块.py

import pyarrow
import pytest

from mymodule import HdfsSearch

@pytest.fixture()
def hdfs_connection_fixture(mocker):
    mocker.patch("pyarrow.hdfs.connect")
    yield HdfsSearch()

def test_hdfs_connection(hdfs_connection_fixture):
    pyarrow.hdfs.connect.assert_called_once() # <-- succeeds

def test_hdfs_search(hdfs_connection_fixture):
    hdfs_connection_fixture.search(".")
    pyarrow.hdfs.HadoopFileSystem.ls.assert_called_once() # <-- fails

pytest 输出:

$ python -m pytest --verbose test_module.py
=========================================================================================================== test session starts ============================================================================================================
platform linux -- Python 3.7.4, pytest-5.0.1, py-1.8.0, pluggy-0.12.0 -- /home/bbaur/miniconda3/envs/dev/bin/python
cachedir: .pytest_cache
rootdir: /home/user1/work/app
plugins: cov-2.7.1, mock-1.10.4
collected 2 items

test_module.py::test_hdfs_connection PASSED                                                                                                                                                                                          [ 50%]
test_module.py::test_hdfs_search FAILED                                                                                                                                                                                              [100%]

================================================================================================================= FAILURES =================================================================================================================
_____________________________________________________________________________________________________________ test_hdfs_search _____________________________________________________________________________________________________________

hdfs_connection_fixture = <mymodule.HdfsSearch object at 0x7fdb4ec2a610>

    def test_hdfs_search(hdfs_connection_fixture):
        hdfs_connection_fixture.search(".")
>       pyarrow.hdfs.HadoopFileSystem.ls.assert_called_once()
E       AttributeError: 'function' object has no attribute 'assert_called_once'

test_module.py:16: AttributeError

最佳答案

您没有在 Mock 对象上调用断言,这是正确的断言:

hdfs_connection_fixture.fs.ls.assert_called_once()

解释:

当您访问 Mock 对象中的任何属性时,它将返回另一个 Mock 对象。

由于您修补了 "pyarrow.hdfs.connect",您已将其替换为 Mock,我们称其为 Mock A。您的 _connect 方法将返回该 Mock A然后将其分配给 self.fs

现在让我们分析一下调用 self.fs.lssearch 方法中发生的事情。

self.fs 返回您的 Mock A 对象,然后 .ls 将返回一个不同的 Mock 对象,我们称它为 Mock B。在这个 Mock B 对象中,您重新调用传递 (path=path)

在您的断言中,您正在尝试访问 pyarrow.hdfs.HadoopFileSystem,但它从未被修补过。您需要对 Mock B 对象执行断言,该对象位于 hdfs_connection_fixture.fs.ls

修补什么

如果您将 mymodule.py 中的导入更改为此 from pyarrow.hdfs import connect,您的补丁将停止工作。

这是为什么呢?

当您修补某些东西时,您正在更改 name 指向的内容,而不是实际的对象。

您当前的补丁正在修补名称 pyarrow.hdfs.connect 并且在 mymodule 中您使用相同的名称 pyarrow.hdfs.connect 所以一切都很好。

但是,如果您使用 from pyarrow.hdfs import connect,mymodule 将导入真正的 pyarrow.hdfs.connect 并为其创建一个名为 的引用>mymodule.connect

因此,当您在 mymodule 中调用 connect 时,您正在访问名称 mymodule.connect,它没有被修补。

这就是为什么在使用 from import 时需要修补 mymodule.connect

我建议在进行此类修补时使用 from x import y。它使您试图模拟的内容更加明确,并且补丁将仅限于该模块,这可以防止不可预见的副作用。

来源,Python 文档中的这一部分:Where to patch

关于python - Pytest 模拟补丁 - 如何排除故障?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57928825/

相关文章:

database - 在没有库的情况下如何模拟数据库调用?

docker - localstack docker 给出错误权限被拒绝 : '/tmp/localstack/server.test.pem.key'

c# - 如何模拟扩展 IEnumerable 的接口(interface)

python - 如何将 stdlib 日志记录与 py.test 结合起来

pytest - 当 pytest 测试失败时是否可以抑制所有参数化参数的显示

python - 根据正则表达式中的相同值选择组

python - 将spark转换为pandas数据帧有异常: arrow is not supported when using file-based collect

python - 使用 Mock.side_effect 作为实例方法

python - 如何测试 Connexion/Flask 应用程序?

python - 部署到 heroku 更改 DEBUG = False 导致 500 错误