簡體   English   中英

如何在 Pytest 中模擬 httpx.AsyncClient()

[英]How to mock httpx.AsyncClient() in Pytest

我需要為 function 編寫測試用例,它用於從 API 獲取數據。在那里我使用 httpx.AsyncClient() 作為上下文管理器。 但我不明白如何為 function 編寫測試用例。

async def make_dropbox_request(url, payload, dropbox_token):
async with httpx.AsyncClient(timeout=None, follow_redirects=True) as client:
    headers = {
        'Content-Type': 'application/json',
        'authorization': 'Bearer '+ dropbox_token
    }
    # make the api call
    response = await client.post(url, headers=headers, json=payload)
    
    if response.status_code not in [200]:
        print('Dropbox Status Code: ' + str(response.status_code))

    if response.status_code in [200, 202, 303]:
        return json.loads(response.text)

    elif response.status_code == 401:
        raise DropboxAuthenticationError()

    elif response.status_code == 429:
        sleep_time = int(response.headers['Retry-After'])
        if sleep_time < 1*60:
            await asyncio.sleep(sleep_time)
            raise DropboxMaxRateLimitError()
        raise DropboxMaxDailyRateLimitError()

    raise DropboxHTTPError()

我需要在不調用 API 的情況下編寫測試用例。所以我相信在這種情況下我需要模擬client.post()但我不知道該怎么做。 如果有人可以幫助我解決這個問題,那對我來說真的很有幫助。

此圖像還包括我的代碼塊

TL;DR:使用return_value.__aenter__.return_value模擬異步上下文。

假設您使用的是Pytestpytest-mock ,您可以使用mocker夾具來模擬httpx.AsyncClient

由於post function 是異步的,因此您需要使用AsyncMock 最后,由於您使用異步上下文,您還需要使用return_value.__aenter__.return_value來正確模擬返回的上下文。 注意同步上下文,只需使用__enter__而不是__aenter__

@pytest.fixture
def mock_AsyncClient(mocker: MockerFixture) -> Mock:
      mocked_AsyncClient = mocker.patch(f"{TESTED_MODULE}.AsyncClient")

      mocked_async_client = Mock()
      response = Response(status_code=200)
      mocked_async_client.post = AsyncMock(return_value=response)
      mocked_AsyncClient.return_value.__aenter__.return_value = mocked_async_client

      return mocked_async_client

我也面臨同樣的問題並用patch裝飾器處理它。 我分享我的代碼,這樣可能對其他人有幫助。

from unittest.mock import patch
import pytest
import httpx
from app.services import your_service


@pytest.mark.anyio
@patch(
    'app.services.your_service.httpx.AsyncClient.post',
    return_value = httpx.Response(200, json={'id': '9ed7dasdasd-08ff-4ae1-8952-37e3a323eb08'})
)
async def test_get_id(mocker):        
    result = await your_service.get_id()
    assert result == '9ed7dasdasd-08ff-4ae1-8952-37e3a323eb08'

您可以試用RESPX mocking 庫來測試和模擬您的HTTPX客戶端。 在您的情況下,應該這樣做:

async def make_dropbox_request(url, payload, dropbox_token):
    ...
    response = await client.post(url, headers=headers, json=payload)
    ...
    return response.json()
​
​
@respx.mock
async def test_dropbox_endpoint():
    url = "https://dropbox-api/some-endpoint/"
    endpoint = respx.post(url).respond(json={"some": "data"})
    result = await make_dropbox_request(url, ..., ...)
    assert endpoint.called
    assert result == {"some": "data"}

為了干燥而不是在每個測試中重復 mocking,您可以設置自己的pytest夾具或 respx 實例,全局預模擬所有保管箱 api 端點,然后在每個測試中根據場景更改響應/錯誤測試,以獲得make_dropbox_request的完整測試覆蓋率。

@pytest.fixture()
async def dropbox_mock():
    async with respx.mock() as dropbox:
        # default endpoints and their responses
        dropbox.post("some-endpoint", name="foo").respond(404)
        dropbox.post("some-other-endpoint", name="bar").respond(404)
        #                                     ^ name routes for access in tests
        yield dropbox
​
​
async def test_some_case(dropbox_mock):
    dropbox_mock["foo"].respond(json={})
    ....

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM