使用 unittest.mock.patch 测试 aiohttp 客户端
Testing aiohttp client with unittest.mock.patch
我已经使用 aiohttp 编写了一个简单的 HTTP 客户端,我正在尝试通过修补 aiohttp.ClientSession
和 aiohttp.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'",如果它 没有被 嘲笑,它应该是这样。)
我无法逃避这种行为。我试过:
- 模拟完成后导入
is_ok
。
- 将模拟分配给
mock_client
和mock_client.__aenter__
、将mock_client.request
设置为MagicMock(return_value=mock_response)
或使用mock_client().request
等的各种组合
- 使用特定的
__aenter__
和 __aexit__
方法编写模拟 ClientSession
,并在 patch
的 new
参数中使用它。
其中 None 似乎有所作为。如果我将断言放入 is_ok
以测试 ClientSession
是 MagicMock
的实例,那么当我 运行 测试时这些断言会失败(同样,当代码未打补丁)。这引出了我的命名空间不匹配理论:也就是说,事件循环 运行 在 patch
所针对的不同命名空间中。
要么,要么我在做傻事!
不鼓励嘲笑 ClientSession
。
推荐的方法是创建假服务器并向其发送真实请求。
我已经使用 aiohttp 编写了一个简单的 HTTP 客户端,我正在尝试通过修补 aiohttp.ClientSession
和 aiohttp.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'",如果它 没有被 嘲笑,它应该是这样。)
我无法逃避这种行为。我试过:
- 模拟完成后导入
is_ok
。 - 将模拟分配给
mock_client
和mock_client.__aenter__
、将mock_client.request
设置为MagicMock(return_value=mock_response)
或使用mock_client().request
等的各种组合 - 使用特定的
__aenter__
和__aexit__
方法编写模拟ClientSession
,并在patch
的new
参数中使用它。
None 似乎有所作为。如果我将断言放入 is_ok
以测试 ClientSession
是 MagicMock
的实例,那么当我 运行 测试时这些断言会失败(同样,当代码未打补丁)。这引出了我的命名空间不匹配理论:也就是说,事件循环 运行 在 patch
所针对的不同命名空间中。
要么,要么我在做傻事!
不鼓励嘲笑 ClientSession
。
推荐的方法是创建假服务器并向其发送真实请求。