无法在 pytest 中使用 pytest-mock 同时验证构造和实例方法调用
Unable to Use pytest-mock in pytest to Verify Construction and Instance Method Calls Simultaneously
首先,我使用 pytest-mock 和 pytest 包来测试我正在处理的软件项目。
我有一种情况,我正在模块中测试 class 并尝试模拟它从其 __init__
方法实例化的复合 object。当我这样做时,我能够断言 __init__
方法被正确调用,但是当我尝试断言实例方法被调用时,我得到一个断言错误。
当我通过将 --pdb
选项传递给它来调试 pytest 时,我通过检查 MagicMock()
object 的 .method.called
属性 来验证它"wasn't" 调用了,但是当我检查 MagicMock()
object 的 .method_calls
属性 时,我发现它实际上被调用了。
这里有一个简单的例子来说明我的意思:
test.py
import module
def test_Calling(mocker):
mocker.patch('module.Called', autospec=True)
module.Calling('argument')
module.Called.assert_called_once_with('argument')
module.Called.check_me.assert_called_once()
module.py
class Calling(object):
def __init__(self, argument):
called_instance = Called(argument)
called_instance.check_me()
class Called(object):
def __init__(self, argument):
pass
def check_me(self):
pass
这里是 pytest/pdb session:
(venv) C:\pytest-issue>pytest --pdb test.py
============================= test session starts =============================
platform win32 -- Python 2.7.12, pytest-3.0.6, py-1.4.32, pluggy-0.4.0
rootdir: C:\pytest-issue, inifile:
plugins: mock-1.5.0
collected 1 items
test.py F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
mocker = <pytest_mock.MockFixture object at 0x000000000431B2E8>
def test_Calling(mocker):
mocker.patch('module.Called', autospec=True)
module.Calling('argument')
module.Called.assert_called_once_with('argument')
> module.Called.check_me.assert_called_once()
test.py:9:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_mock_self = <MagicMock name='Called.check_me' spec='instancemethod' id='70367608'>
def assert_called_once(_mock_self):
"""assert that the mock was called only once.
"""
self = _mock_self
if not self.call_count == 1:
msg = ("Expected '%s' to have been called once. Called %s times." %
(self._mock_name or 'mock', self.call_count))
> raise AssertionError(msg)
E AssertionError: Expected 'check_me' to have been called once. Called 0 times.
venv\lib\site-packages\mock\mock.py:915: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> c:\pytest-issue\venv\lib\site-packages\mock\mock.py(915)assert_called_once()
-> raise AssertionError(msg)
(Pdb) up
> c:\pytest-issue\test.py(9)test_Calling()
-> module.Called.check_me.assert_called_once()
(Pdb) module.Called.method_calls
[call().check_me()]
(Pdb) module.Called.check_me.called
False
对于我的一生,我不确定自己做错了什么。在这种情况下,我需要断言.Called.method_calls
吗?如果是这样,那看起来我将不得不在我的测试中加入大量逻辑,这是不可取的。
所以,事实证明我对你如何测试补丁模块一无所知。
import module
def test_Calling(mocker):
mock_Called = mocker.patch('module.Called')
module.Calling('argument')
mock_called.assert_has_calls([
mocker.call('argument'),
mocker.call().check_me()])
这里要考虑的重要事项是您需要存储 mocker.patch 的 return 值,因为这将提供对实例化和修补的 MagicMock
对象的引用在超过 module.Called
。接下来,当您希望测试实例方法是否被调用时,您应该在 MagicMock
实例上使用 assert_has_calls
方法,并向其传递反映所调用内容的 call()
参数。 call()
本身指的是 __new__/__init__
调用,call().method
指的是对实例方法的调用。
自从弄清楚这一点后,我就能够非常成功地使用 pytest-mock
模块。
首先,我使用 pytest-mock 和 pytest 包来测试我正在处理的软件项目。
我有一种情况,我正在模块中测试 class 并尝试模拟它从其 __init__
方法实例化的复合 object。当我这样做时,我能够断言 __init__
方法被正确调用,但是当我尝试断言实例方法被调用时,我得到一个断言错误。
当我通过将 --pdb
选项传递给它来调试 pytest 时,我通过检查 MagicMock()
object 的 .method.called
属性 来验证它"wasn't" 调用了,但是当我检查 MagicMock()
object 的 .method_calls
属性 时,我发现它实际上被调用了。
这里有一个简单的例子来说明我的意思:
test.py
import module
def test_Calling(mocker):
mocker.patch('module.Called', autospec=True)
module.Calling('argument')
module.Called.assert_called_once_with('argument')
module.Called.check_me.assert_called_once()
module.py
class Calling(object):
def __init__(self, argument):
called_instance = Called(argument)
called_instance.check_me()
class Called(object):
def __init__(self, argument):
pass
def check_me(self):
pass
这里是 pytest/pdb session:
(venv) C:\pytest-issue>pytest --pdb test.py
============================= test session starts =============================
platform win32 -- Python 2.7.12, pytest-3.0.6, py-1.4.32, pluggy-0.4.0
rootdir: C:\pytest-issue, inifile:
plugins: mock-1.5.0
collected 1 items
test.py F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
mocker = <pytest_mock.MockFixture object at 0x000000000431B2E8>
def test_Calling(mocker):
mocker.patch('module.Called', autospec=True)
module.Calling('argument')
module.Called.assert_called_once_with('argument')
> module.Called.check_me.assert_called_once()
test.py:9:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_mock_self = <MagicMock name='Called.check_me' spec='instancemethod' id='70367608'>
def assert_called_once(_mock_self):
"""assert that the mock was called only once.
"""
self = _mock_self
if not self.call_count == 1:
msg = ("Expected '%s' to have been called once. Called %s times." %
(self._mock_name or 'mock', self.call_count))
> raise AssertionError(msg)
E AssertionError: Expected 'check_me' to have been called once. Called 0 times.
venv\lib\site-packages\mock\mock.py:915: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> c:\pytest-issue\venv\lib\site-packages\mock\mock.py(915)assert_called_once()
-> raise AssertionError(msg)
(Pdb) up
> c:\pytest-issue\test.py(9)test_Calling()
-> module.Called.check_me.assert_called_once()
(Pdb) module.Called.method_calls
[call().check_me()]
(Pdb) module.Called.check_me.called
False
对于我的一生,我不确定自己做错了什么。在这种情况下,我需要断言.Called.method_calls
吗?如果是这样,那看起来我将不得不在我的测试中加入大量逻辑,这是不可取的。
所以,事实证明我对你如何测试补丁模块一无所知。
import module
def test_Calling(mocker):
mock_Called = mocker.patch('module.Called')
module.Calling('argument')
mock_called.assert_has_calls([
mocker.call('argument'),
mocker.call().check_me()])
这里要考虑的重要事项是您需要存储 mocker.patch 的 return 值,因为这将提供对实例化和修补的 MagicMock
对象的引用在超过 module.Called
。接下来,当您希望测试实例方法是否被调用时,您应该在 MagicMock
实例上使用 assert_has_calls
方法,并向其传递反映所调用内容的 call()
参数。 call()
本身指的是 __new__/__init__
调用,call().method
指的是对实例方法的调用。
自从弄清楚这一点后,我就能够非常成功地使用 pytest-mock
模块。