Val*_*lev 7 python mocking pytest
我正在解决以下问题,我有一个类,一个我想模拟补丁的异步方法:
class ExampleClass:
async def asy_method(self, param):
return await some_coroutine(self, param)
example_instance = ExampleClass()
Run Code Online (Sandbox Code Playgroud)
我想专门修补像这样的调用
await example_instance.asy_method('test_param')
Run Code Online (Sandbox Code Playgroud)
通常我会用
mocker.patch('ExampleClass.asy_method', new_callable=AsyncMock)
Run Code Online (Sandbox Code Playgroud)
其中 mocker 是 pytest-mock 插件夹具,而 AsyncMock 具有以下形式
class AsyncMock(mock.MagicMock):
async def __call__(self, *args, **kwargs):
return super(AsyncMock, self).__call__(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
这会给我一个 Mock 对象,它的行为就像一个调用时的协程。问题是,我想访问self
传递给该方法的属性。self
仅在您设置时才传递给模拟对象
autospec=True
(另请参阅有关修补未绑定方法的 Python 文档),您不能将其与new_callable
.
有谁知道如何解决这个问题?
实际上,您不能将自动指定和新的可调用对象混合使用。相反,自动指定方法,然后替换side_effect
属性,给它一个AsyncMock()
实例:
from unittest import mock
def configure_coroutine_mock(mock_function, klass=AsyncMock):
"""Make an autospecced async function return a coroutine mock"""
mock_function.side_effect = AsyncMock()
# mark the side effect as a child of the original mock object
# so transitive access is recorded on the parent mock too. This is
# what .return_value does normally
mock._check_and_set_parent(
mock_function.mock, mock_function.side_effect,
None, '()')
return mock_asy_method.side_effect
with mocker.patch('ExampleClass.asy_method', autospec=True) as mock_asy_method:
configure_coroutine_mock(mock_asy_method)
Run Code Online (Sandbox Code Playgroud)
因为AsyncMock()
是一个可调用对象,所以每次被调用时mock_asy_method
都会调用它,并将参数传递给对象。然后使用该调用的结果从 返回mock_asy_method()
:
>>> from unittest import mock
>>> class ExampleClass:
... async def asy_method(self, param):
... return await some_coroutine(self, param)
...
>>> example_instance = ExampleClass()
>>> with mock.patch('__main__.ExampleClass.asy_method', autospec=True) as mock_asy_method:
... configure_coroutine_mock(mock_asy_method)
... print(example_instance.asy_method('foo')) # call to patched class coroutine
... print(mock_asy_method.mock_calls) # calls are recorded
...
<AsyncMock name='asy_method()' id='4563887496'>
<coroutine object AsyncMock.__call__ at 0x1100780f8>
[call(<__main__.ExampleClass object at 0x10ffac1d0>, 'foo')]
Run Code Online (Sandbox Code Playgroud)
如您所见,self
参数和参数都记录在调用中,因为它mock_asy_method
是一个正确指定的函数。
当然,只有在AsyncMock()
实际等待返回的调用结果时,我们才会看到该调用也被记录了:
>>> with mock.patch('__main__.ExampleClass.asy_method', autospec=True) as mock_asy_method:
... configure_coroutine_mock(mock_asy_method)
... loop = asyncio.get_event_loop()
... coro = example_instance.asy_method('foo')
... loop.run_until_complete(coro)
... print(mock_asy_method.mock_calls)
...
<AsyncMock name='asy_method()' id='4564408920'>
<AsyncMock name='asy_method()()' id='4564999360'>
[call(<__main__.ExampleClass object at 0x10ffac1d0>, 'foo'),
call()(<__main__.ExampleClass object at 0x10ffac1d0>, 'foo')]
Run Code Online (Sandbox Code Playgroud)