我遇到了一个我认为是使用模拟补丁的常见问题,因为我无法找出正确的东西来补丁。
我有两个问题希望得到帮助。
- 关于如何解决以下示例中的特定问题的想法
- 可能最重要的是关于如何最好地解决“我应该修补哪个”问题的专业提示/指示/想法/建议。我遇到的问题是,如果不完全了解补丁的工作原理,我什至不知道我应该寻找什么,结果发现自己在玩猜谜游戏。
一个使用 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.ls
时 search
方法中发生的事情。
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/