python - 如何修补异步类方法?

标签 python mocking pytest

我正在处理以下问题,我有一个类,一个我想模拟补丁的异步方法:

class ExampleClass:
    async def asy_method(self, param):
        return await some_coroutine(self, param)

example_instance = ExampleClass()

我只想专门修补像这样的调用

await example_instance.asy_method('test_param')

通常我会用

mocker.patch('ExampleClass.asy_method', new_callable=AsyncMock)

其中 mocker 是 pytest-mock 插件 fixture ,AsyncMock 具有以下形式

class AsyncMock(mock.MagicMock):
    async def __call__(self, *args, **kwargs):
         return super(AsyncMock, self).__call__(*args, **kwargs)

这会给我一个 Mock 对象,它的行为就像一个随叫随到的协程。 问题是,我想访问传递给方法的 self 属性。 self 仅在您设置时传递给模拟对象 autospec=True 但是(另请参阅 Python Doc on patching unbound methods ),您不能将其与 new_callable 一起使用。

有没有人知道如何解决这个问题?

最佳答案

确实,您不能混合使用自动指定和新的可调用对象。相反,autospec 方法,然后替换 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)

因为 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')]

如您所见,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')]

关于python - 如何修补异步类方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50991740/

相关文章:

python - 根据其他列在 df 中创建类别

Python单元测试模拟,获取模拟函数的输入参数

Angular2 组件测试 - 错误 : Can't resolve all parameters

unit-testing - 行为与基于状态的测试

python - 限制pytest中执行的测试用例数量

python - 从 pytest.main() 输出 stdio 和 stderr

python - 从 Flask 框架编写 CSV

python - 如何使用 np.average 包含 np.nan 来计算平均值? (使用groupby时)

python - 在Python中读取csv文件时的整数

python - py.test 在类下找不到测试