Skip to main content

Async mock HTTP server for pytest, built on top of aiohttp.

Project description

async-pytest-httpserver

PyPI PyPI Downloads

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

async_pytest_httpserver-1.0.0.tar.gz (68.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

async_pytest_httpserver-1.0.0-py3-none-any.whl (5.8 kB view details)

Uploaded Python 3

File details

Details for the file async_pytest_httpserver-1.0.0.tar.gz.

File metadata

File hashes

Hashes for async_pytest_httpserver-1.0.0.tar.gz
Algorithm Hash digest
SHA256 90141ca75094e863e28237e2efccf6e404a5b8197bd8925bf23cd84946eb5d50
MD5 1b5e848f618846eaafa36b4892ab0f60
BLAKE2b-256 a86ccaa7ae42694144d2721bde1cc64fb99b997a78f7956ef8d020cf79862596

See more details on using hashes here.

File details

Details for the file async_pytest_httpserver-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for async_pytest_httpserver-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b8a255e71686db478d4495aaa28df85b384673ac3c9c0be4c187505fba1d50f8
MD5 99ddf9291803ac094cc89f8af50c28cb
BLAKE2b-256 3dabb6e8bd82efe22a908907f2177389595d2e2101a7b68921472439c78fc3cf

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page