Colin-b / pytest_httpx

pytest fixture to mock HTTPX
https://colin-b.github.io/pytest_httpx/
MIT License
359 stars 30 forks source link

No longer works with starlette/fastapi TestClient #165

Open sscherfke opened 2 weeks ago

sscherfke commented 2 weeks ago

Follow-up of https://github.com/Colin-b/pytest_httpx/issues/144#issuecomment-2071887265


The following example works with pytest-httpx 0.26 but fails with pytest-httpx 0.30:

import fastapi
import httpx
import pytest
import pytest_asyncio
import pytest_httpx

@pytest_asyncio.fixture
async def client() -> fastapi.FastAPI:
    app = fastapi.FastAPI()

    @app.get("/")
    def get() -> None:
        return None

    async with httpx.AsyncClient(
        transport=httpx.ASGITransport(app=app),
        base_url="http://test",
    ) as api_client:
        yield api_client

@pytest.mark.asyncio
async def test_ok(client: httpx.AsyncClient) -> None:
    response = await client.get("/")
    assert response.status_code == 200

@pytest.mark.asyncio
async def test_err(
    client: httpx.AsyncClient, httpx_mock: pytest_httpx.HTTPXMock
) -> None:
    httpx_mock.add_response(status_code=500, text="Internal Server Error")
    response = await client.get("/")
    assert response.status_code == 500

httpx.ASGITransport inherits httpx.AsyncBaseTransport and pytest-httpx only mocks httpx.AsyncHTTPTransport.

I don't think it would be a good idea to mock the base transport, because its handle-Method is "abstract" and should remain so.

Maybe it should mock the asgi/wsgi transport in addition to the http transport or allow passing a list of transport classes to mock.

The Starlette test client inherts httpx.BaseTransport: https://github.com/encode/starlette/blob/master/starlette/testclient.py#L239

My current workaround is this fixture:

@pytest.fixture
def httpx_testclient_mock(monkeypatch: pytest.MonkeyPatch) -> HTTPXMock:
    # This fixture is a workaound for
    # https://github.com/Colin-b/pytest_httpx/issues/144
    mock = HTTPXMock()

    def mocked_handle_request(
        transport: httpx.HTTPTransport, request: httpx.Request
    ) -> httpx.Response:
        return mock._handle_request(transport, request)  # noqa: SLF001

    monkeypatch.setattr(
        starlette.testclient._TestClientTransport,  # noqa: SLF001
        "handle_request",
        mocked_handle_request,
    )

    return mock
Colin-b commented 2 weeks ago

Hello @sscherfke

This usage was not part of the non regression suite, and was never documented. However I agree it would be a good addition if most parameters still make sense.

I will have a look at it and see what can be done here.

Thanks for reporting

sscherfke commented 2 weeks ago

Thx, let me know if I can help you with anything <3