问题描述
我已经使用 aiohttp 编写了一个简单的 HTTP 客户端,我正在尝试通过修补 aiohttp.ClientSession
和 aiohttp.ClientResponse
来测试它.但是,看起来 unittest.mock.patch
装饰器似乎不尊重我的异步代码.猜测,我会说这是某种命名空间不匹配.
I've written a simple HTTP client using aiohttp and I'm trying to test it by patching aiohttp.ClientSession
and aiohttp.ClientResponse
. However, it appears as though the unittest.mock.patch
decorator is not respecting my asynchronous code. At a guess, I would say it's some kind of namespacing mismatch.
这是一个最小的例子:
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)
我正在使用异步装饰器进行测试,如此答案中所述.所以这是我尝试的测试:
I'm using an asynchronous decorator for testing, as described in this answer. So here's my attempted test:
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
调用所模拟.(特别是它说无法从 URL 'foo' 解析主机名",如果它没有被嘲笑,它应该这样做.)
My is_ok
coroutine works fine when it's used in, say, __main__
, but when I run the test, it gives me an error that indicates that the session.request
function has not been mocked per my patch
call. (Specifically it says "Could not parse hostname from URL 'foo'", which it should if it weren't mocked.)
我无法逃避这种行为.我试过了:
I am unable to escape this behaviour. I have tried:
- 在模拟完成后导入
is_ok
. - 将模拟分配给
mock_client
和mock_client.__aenter__
的各种组合,将mock_client.request
设置为MagicMock(return_value=mock_response)
,或使用mock_client().request
等 - 使用特定的
__aenter__
和__aexit__
方法编写模拟ClientSession
并在new
参数中使用它 .
- Importing
is_ok
after the mocking is done. - Various combinations of assigning mocks to
mock_client
andmock_client.__aenter__
, settingmock_client.request
toMagicMock(return_value=mock_response)
, or usingmock_client().request
, etc. - Writing a mock
ClientSession
with specific__aenter__
and__aexit__
methods and using it in thenew
argument topatch
.
这些似乎都没有区别.如果我将断言放入 is_ok
以测试 ClientSession
是 MagicMock
的一个实例,那么当我运行测试时这些断言将失败(因为,再次,当代码没有打补丁时他们会这样做).这引出了我的命名空间不匹配理论:也就是说,事件循环在 patch
所针对的不同命名空间中运行.
None of these appear to make a difference. If I put assertions into is_ok
to test that ClientSession
is an instance of MagicMock
, then these assertions fail when I run the test (as, again, they would when the code is not patched). That leads me to my namespacing mismatch theory: That is, the event loop is running in a different namespace to which patch
is targeting.
要么那样,要么我在做一些愚蠢的事情!
Either that, or I'm doing something stupid!
推荐答案
不鼓励模拟 ClientSession
.
推荐的方法是创建假服务器并向其发送真实请求.
Recommended way is creation fake server and sending real requests to it.
查看 aiohttp 示例.
这篇关于使用 unittest.mock.patch 测试 aiohttp 客户端的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!