如何在 Python 测试中模拟 Awaitable

How to mock an Awaitable in Python tests

我想知道是否有一种方法可以轻松模拟 Awaitable 对象以进行 python 测试。我知道如何使用 AsyncMocks 创建镜像协程(即从方法调用中 return Awaitable 对象),但是 运行 在尝试时遇到了一些问题直接模拟 Awaitable

这是一个例子:

import unittest
from asyncio import Task
from typing import Any
from unittest import IsolatedAsyncioTestCase
from unittest.mock import AsyncMock


async def function_using_awaitable(task: Task) -> Any:
    await task
    return task.result()

class TestFunction(IsolatedAsyncioTestCase):
    async def test_function_using_awaitable(self):
        mock_task = AsyncMock(spec=Task)

        result = await function_using_awaitable(mock_task)

        mock_task.assert_awaited_once()
        assert result == mock_task.result.return_value

if __name__ == '__main__':
    unittest.main()

抛出:

Traceback (most recent call last):
  ...
  File ..., line 9, in function_using_awaitable
    await task
TypeError: object AsyncMock can't be used in 'await' expression

深入研究 Python 文档,看起来 __await__ 魔术方法是模拟中不支持的少数方法之一,所以想知道我该如何去做?

一个解决方案可能是将 AsyncMock 扩展为可等待的模拟对象,添加 await。在代码中,可能看起来像

from typing import Iterator, Any

async def function_using_awaitable(task: Task) -> Any:
    await task
    return task.result()

class AwaitableMock(AsyncMock):
    def __await__(self) -> Iterator[Any]:
        self.await_count += 1
        return iter([])

class TestFunction(IsolatedAsyncioTestCase):
    async def test_function_using_awaitable(self):
        mock_task = AwaitableMock(spec=Task)

        result = await function_using_awaitable(mock_task)

        mock_task.assert_awaited_once()
        assert result == mock_task.result.return_value