使用 unittest.mock.patch 测试 aiohttp 客户端

Testing aiohttp client with unittest.mock.patch

我已经使用 aiohttp 编写了一个简单的 HTTP 客户端,我正在尝试通过修补 aiohttp.ClientSessionaiohttp.ClientResponse 来测试它。但是,似乎 unittest.mock.patch 装饰器没有遵守我的异步代码。猜测,我会说这是某种命名空间不匹配。

这是一个最小的例子:

from aiohttp import ClientSession

async def is_ok(url:str) -> bool:
    async with ClientSession() as session:
        async with session.request("GET", url) as response:
            return (response.status == 200)

我正在使用异步装饰器进行测试,如 this answer 中所述。所以这是我尝试的测试:

import unittest
from unittest.mock import MagicMock, patch

from aiohttp import ClientResponse

from my.original.module import is_ok

class TestClient(unittest.TestCase):
    @async_test
    @patch("my.original.module.ClientSession", spec=True)
    async def test_client(self, mock_client):
        mock_response = MagicMock(spec=ClientResponse)
        mock_response.status = 200

        async def _mock_request(*args, **kwargs):
            return mock_response

        mock_client.request = mock_response

        status = await is_ok("foo")
        self.assertTrue(status)

我的 is_ok 协同程序在 __main__ 中使用时工作正常,但是当我 运行 测试时,它给我一个错误,表明 session.request 函数未根据我的 patch 调用进行模拟。 (具体来说,它说 "Could not parse hostname from URL 'foo'",如果它 没有被 嘲笑,它应该是这样。)

我无法逃避这种行为。我试过:

其中

None 似乎有所作为。如果我将断言放入 is_ok 以测试 ClientSessionMagicMock 的实例,那么当我 运行 测试时这些断言会失败(同样,当代码未打补丁)。这引出了我的命名空间不匹配理论:也就是说,事件循环 运行 在 patch 所针对的不同命名空间中。

要么,要么我在做傻事!

不鼓励嘲笑 ClientSession

推荐的方法是创建假服务器并向其发送真实请求。

看看aiohttp example