Metadata-Version: 2.5
Name: aiointercept
Version: 1.0.0
Summary: aiohttp mock library that routes requests through a real test server
Project-URL: Homepage, https://github.com/Polandia94/aiointercept
Project-URL: Repository, https://github.com/Polandia94/aiointercept
Project-URL: Issues, https://github.com/Polandia94/aiointercept/issues
Author-email: Pablo Estevez <pablo22estevez@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: aiohttp,aioresponses,asyncio,mock,pytest,testing
Classifier: Development Status :: 5 - Production/Stable
Classifier: Framework :: AsyncIO
Classifier: Framework :: Pytest
Classifier: Framework :: aiohttp
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Software Development :: Testing :: Mocking
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: aiohttp<4.0.0,>=3.13.3
Requires-Dist: multidict<8.0,>=6.0
Requires-Dist: yarl<2.0.0,>=1.23.0
Provides-Extra: pytest-asyncio
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'pytest-asyncio'
Description-Content-Type: text/markdown

# aiointercept

[![PyPI](https://img.shields.io/pypi/v/aiointercept.svg)](https://pypi.org/project/aiointercept/)
[![Python](https://img.shields.io/pypi/pyversions/aiointercept.svg)](https://pypi.org/project/aiointercept/)
[![Docs](https://readthedocs.org/projects/aiointercept/badge/?version=latest)](https://aiointercept.readthedocs.io/)
[![CI](https://github.com/Polandia94/aiointercept/actions/workflows/ci.yml/badge.svg)](https://github.com/Polandia94/aiointercept/actions)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)

Mock `aiohttp` HTTP requests by routing them through a real `aiohttp.web` test server. Inspired by `aioresponses`, with a largely compatible API.

```python
import aiohttp
from aiointercept import aiointercept

async with aiointercept() as m:
    m.get(f"{m.server_url}/users", payload=[{"id": 1}])

    async with aiohttp.ClientSession() as session:
        resp = await session.get(f"{m.server_url}/users")
        assert await resp.json() == [{"id": 1}]
```

## Why aiointercept?

Testing code that makes HTTP requests usually means either hitting a real server (slow, fragile, requires network) or replacing the HTTP layer with fake objects (fast, but disconnected from reality).

`aiointercept` takes a third path: it starts a real `aiohttp.web` server on localhost and redirects your client's requests to it — either by pointing the client at `m.server_url` directly, or by patching the DNS resolver so existing URLs are transparently intercepted. Your code runs its full HTTP stack; only the remote endpoint is replaced.

- **Real serialization.** Headers, body encoding, and content-type negotiation all go through the actual aiohttp stack.
- **Inspectable requests.** Callbacks receive a real `aiohttp.web.Request` — read the body, headers, and query params the server saw.
- **Minimal patching.** The default mode touches nothing globally. When you need to intercept hardcoded URLs, only the DNS resolver is patched, so redirects and connection pooling still behave as in production.

## Using other HTTP clients

With the default `mock_external_urls=False`, the mock is a plain HTTP server, so `requests`, `httpx` (sync or async), or any other client can call `m.server_url`. The server runs on its own thread, so blocking clients work directly from async tests with static responses or synchronous callbacks. When using an async callback, offload the blocking client call so the test's event loop remains available:

```python
import httpx
import requests

async with aiointercept() as m:
    m.get(f"{m.server_url}/users", payload=[{"id": 1}], repeat=True)

    assert requests.get(f"{m.server_url}/users").json() == [{"id": 1}]

    async with httpx.AsyncClient() as client:
        resp = await client.get(f"{m.server_url}/users")
        assert resp.json() == [{"id": 1}]
```

aiointercept is built primarily for `aiohttp`: its API mirrors `aioresponses`, and intercepting hardcoded URLs (`mock_external_urls=True`) only works with aiohttp. 


## Installation

```bash
pip install aiointercept
```

**Requirements:** Python ≥ 3.11, aiohttp ≥ 3.13.

For the bundled pytest fixtures, install the `pytest-asyncio` extra:

```bash
pip install 'aiointercept[pytest-asyncio]'
```

The extra requires pytest-asyncio 0.24 or newer. The plugin registers its
fixtures only when a compatible pytest-asyncio is installed; otherwise it is
inactive.

## Documentation

Full documentation is at **[aiointercept.readthedocs.io](https://aiointercept.readthedocs.io/)**:

- [Quickstart](https://aiointercept.readthedocs.io/en/latest/quickstart.html) — context manager, decorator, pytest fixture, and `start()`/`stop()` usage.
- [Usage guide](https://aiointercept.readthedocs.io/en/latest/usage.html) — interception modes, registering responses, regex patterns, callbacks, and passthrough.
- [Assertions](https://aiointercept.readthedocs.io/en/latest/assertions.html) — `assert_called_with` and friends.
- [API reference](https://aiointercept.readthedocs.io/en/latest/api.html) — every public class and method.

## Sharing the server across tests

Starting and stopping a server for every test adds up. `aiointercept` ships an
auto-discovered pytest plugin that starts the server
**once per session** and hands each test a cleared mock via the
`aiointercept_mock` fixture:

```python
async def test_users(aiointercept_mock):
    m = aiointercept_mock
    m.get(f"{m.server_url}/users", payload=[{"id": 1}])

    async with aiohttp.ClientSession() as session:
        resp = await session.get(f"{m.server_url}/users")
        assert await resp.json() == [{"id": 1}]
```

The fixture calls `m.clear()` between tests, so registered handlers and recorded
requests never leak from one test to the next. The session-scoped server itself
is exposed as `aiointercept_server` if you need it directly.

## Coming from aioresponses?

`aiointercept` aims to be a near drop-in replacement. The [migration guide](https://aiointercept.readthedocs.io/en/latest/migrating.html) ([MIGRATING.md](MIGRATING.md)) covers every breaking change. If you hit an incompatibility it doesn't cover, please [open an issue](https://github.com/Polandia94/aiointercept/issues).

## Contributing

```bash
uv sync --group dev --group tests   # install everything
uv run pytest tests/                 # run the suite
uv run ruff check .                  # lint
uv run mypy aiointercept             # type check
```

Pre-commit hooks run `ruff` and `mypy` on every commit; do not bypass them with `--no-verify`.

## License

`aiointercept` is released under the [MIT License](LICENSE).

## Attribution

Built on ideas and API conventions from [aioresponses](https://github.com/pnuckowski/aioresponses) by Pawel Nuckowski (MIT License). [tests/test_aioresponse.py](tests/test_aioresponse.py) is a lightly adapted port of the original test suite, used to verify compatibility.
