Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot patch the 'httpx' requests while unit testing #62

Open
srbssv opened this issue Dec 28, 2022 · 1 comment
Open

Cannot patch the 'httpx' requests while unit testing #62

srbssv opened this issue Dec 28, 2022 · 1 comment

Comments

@srbssv
Copy link

srbssv commented Dec 28, 2022

Sanic-testing uses httpx under the hood, which makes monkeypatching httpx requests hard to implement.

For example if we have and endpoint which makes external requests, and if we monkeypatch this request, then it patches an actual request to endpoint:

# views.py
import httpx
...
@app.get('/test')
async def view_test(request):
    async with httpx.AsyncClient() as client:
        api_response = await client.get(
            'https://jsonplaceholder.typicode.com/todos/1',
            timeout=10,
        )
        resp = api_response.json()
        resp['foo'] = 0
        return HTTPResponse(json.dumps(resp), 200)

Test function:

# test_views.py
import httpx, pytest
...
# The `client` parameter is the fixture of web app
def test_view_test(client, monkeypatch):
    async def return_mock_response(*args, **kwargs):
        return httpx.Response(200, content=b'{"response": "response"}')

    monkeypatch.setattr(httpx.AsyncClient, 'get', return_mock_response)
    _, response = client.test_client.get('/test')
    assert response.json == {'response': 'response', 'foo': 0}
    assert response.status_code == 200

Here, we replace our request _, response = client.test_client.get('/test') with the patched one. Which makes us customize the patch by excluding the host address.

@ahopkins
Copy link
Member

This might be as simple as exposing a fixture like this:

@pytest.fixture
def httpx():
    orig = AsyncClient.request
    mock = AsyncMock()

    async def request(self, method, url, **kwargs):
        if "127.0.0.1" in url:
            return await orig(self, method, url, **kwargs)

        return await mock(method, url, **kwargs)

    AsyncClient.request = request
    yield mock
    AsyncClient.request = orig

Then ...

def test_outgoing(app: Sanic, httpx: AsyncMock):
    httpx.return_value = Response(201, json={"foo": "bar"})

    _, response = app.test_client.post("")
    assert response.status == 201
    assert response.json == {"foo": "bar"}
    httpx.assert_awaited_once_with(
        "POST",
        "https://httpbin.org/post",
        content=ANY,
        files=ANY,
        data=ANY,
        json=ANY,
        cookies=ANY,
        params=ANY,
        headers=ANY,
        auth=ANY,
        follow_redirects=ANY,
        timeout=ANY,
        extensions=ANY,
    )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants