Async mock HTTP server for pytest, built on top of aiohttp.
Project description
async-pytest-httpserver
No AI was used in the creation of this library.
async-pytest-httpserver is a fully asynchronous mock HTTP server for pytest, built on top of aiohttp.
It is designed for testing code that makes HTTP requests (via aiohttp, httpx, requests, etc.) without depending on real external services.
Features
- Fully asynchronous — implemented using aiohttp
- Dynamic runtime mocking — add or modify mock routes while the server is running
- Seamless pytest integration — works smoothly with pytest-aiohttp and pytest-asyncio
- Real TCP server — compatible with any HTTP client (aiohttp, httpx, requests, etc.)
- Supports async handlers — easily define coroutine-based responses
- Flexible mock responses — either return a Response object or a handler that produces one
How to use
1. fixture for start mock server
from async_pytest_httpserver import (
MockData,
AddMockDataFunc,
)
@pytest_asyncio.fixture
async def some_service_mock(
external_service_mock: Callable[
[], Awaitable[tuple[str, AddMockDataFunc]]
],
) -> AsyncGenerator[AddMockDataFunc, None]:
url, add_mock_data = await external_service_mock()
old_url = settings.EXTERNAL_SERVICE_URL
settings.EXTERNAL_SERVICE_URL = url
try:
yield add_mock_data
finally:
settings.EXTERNAL_SERVICE_URL = old_url
2. mock specific api
You don’t need to follow this pattern exactly — this is just an example where the fixture is responsible for mocking a specific route.
@pytest.fixture
def some_service_mock_api(
some_service_mock: AddMockDataFunc,
) -> Callable[
[web.Response | ResponseHandler],
List[dict[str, Any]],
]:
"""An example of a fixture where a specific API is mocked"""
def _create_mock(
response: web.Response
| Callable[[web.Request], web.Response | Awaitable[web.Response]],
) -> List[dict[str, Any]]:
return some_service_mock(MockData("POST", "/some_api", response))
return _create_mock
3. test it
import pytest
from http import HTTPStatus
from aiohttp.web import json_response, Request, Response
# example of static mock
@pytest.mark.asyncio
async def test_static_mock(client, some_service_mock_api):
# Arrange
calls_info = some_service_mock_api(
json_response(
{"result": "some_result"},
status=HTTPStatus.OK,
)
)
# Act
response = await client.post(
f"{settings.EXTERNAL_SERVICE_URL}/some_api",
json={"text": "text"},
)
# Assert
assert response.ok
data = await response.json()
assert data["result"] == "some_result"
assert len(calls_info) == 1
call_info = calls_info[0]
assert call_info["json"] == {"text": "text"}
# example of dynamic async mock
async def async_mock_handler(request: Request) -> Response:
return json_response(
{"result": "some_result"},
status=HTTPStatus.OK,
)
@pytest.mark.asyncio
async def test_async_handler(client, some_service_mock_api):
# Arrange
calls_info = some_service_mock_api(async_mock_handler)
# Act
response = await client.post(
f"{settings.EXTERNAL_SERVICE_URL}/some_api",
json={"text": "text"},
)
# Assert
assert response.ok
data = await response.json()
assert data["result"] == "some_result"
assert len(calls_info) == 1
call_info = calls_info[0]
assert call_info["json"] == {"text": "text"}
# example of dynamic sync mock
def sync_mock_handler(request: Request) -> Response:
return json_response(
{"result": "some_result"},
status=HTTPStatus.OK,
)
@pytest.mark.asyncio
async def test_sync_handler(client, some_service_mock_api):
# Arrange
calls_info = some_service_mock_api(sync_mock_handler)
# Act
response = await client.post(
f"{settings.EXTERNAL_SERVICE_URL}/some_api",
json={"text": "text"},
)
# Assert
assert response.ok
data = await response.json()
assert data["result"] == "some_result"
assert len(calls_info) == 1
call_info = calls_info[0]
assert call_info["json"] == {"text": "text"}
mock data types
1. just aiohttp.web.Response
for example:
from aiohttp.web import json_response
json_response(
{"result": "some_result"},
status=HTTPStatus.OK,
)
2. callable
If you need custom behavior instead of a static response, you can provide a callable (func or async func) that returns a aiohttp.web.Response.
It must match the following signature:
ResponseHandler = Callable[
[web.Request], web.Response | Awaitable[web.Response]
]
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file async_pytest_httpserver-1.0.0.tar.gz.
File metadata
- Download URL: async_pytest_httpserver-1.0.0.tar.gz
- Upload date:
- Size: 68.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.17
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90141ca75094e863e28237e2efccf6e404a5b8197bd8925bf23cd84946eb5d50
|
|
| MD5 |
1b5e848f618846eaafa36b4892ab0f60
|
|
| BLAKE2b-256 |
a86ccaa7ae42694144d2721bde1cc64fb99b997a78f7956ef8d020cf79862596
|
File details
Details for the file async_pytest_httpserver-1.0.0-py3-none-any.whl.
File metadata
- Download URL: async_pytest_httpserver-1.0.0-py3-none-any.whl
- Upload date:
- Size: 5.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.17
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b8a255e71686db478d4495aaa28df85b384673ac3c9c0be4c187505fba1d50f8
|
|
| MD5 |
99ddf9291803ac094cc89f8af50c28cb
|
|
| BLAKE2b-256 |
3dabb6e8bd82efe22a908907f2177389595d2e2101a7b68921472439c78fc3cf
|