sanic-testing
sanic-testing copied to clipboard
Cannot patch the 'httpx' requests while unit testing
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.
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,
)