本文介绍了使用 unittest.mock.patch 测试 aiohttp 客户端的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经使用 aiohttp 编写了一个简单的 HTTP 客户端,我正在尝试通过修补 aiohttp.ClientSessionaiohttp.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_clientmock_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 and mock_client.__aenter__, setting mock_client.request to MagicMock(return_value=mock_response), or using mock_client().request, etc.
  • Writing a mock ClientSession with specific __aenter__ and __aexit__ methods and using it in the new argument to patch.

这些似乎都没有区别.如果我将断言放入 is_ok 以测试 ClientSessionMagicMock 的一个实例,那么当我运行测试时这些断言将失败(因为,再次,当代码没有打补丁时他们会这样做).这引出了我的命名空间不匹配理论:也就是说,事件循环在 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 客户端的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-22 15:33