This page covers how mocket integrates with Python's asyncio framework and async HTTP clients such as aiohttp. It describes the async_mocketize decorator, the async context manager protocol on Mocketizer, how asyncio.open_connection behaves under mocking, and known considerations when using aiohttp.
For the sync decorator and context manager, see page 4.1. For the MocketTCPConnector aiohttp plugin in isolation, see page 6.2.
Mocket's socket-level monkey-patching is inherently compatible with asyncio because asyncio ultimately calls down to socket-level operations that MocketSocket intercepts. Two additional pieces make async testing ergonomic:
async_mocketize — an async def-compatible decorator that wraps an async test function in a Mocketizer async context.Mocketizer.__aenter__ / __aexit__ — the async context manager protocol on Mocketizer, allowing async with Mocketizer() usage.Neither mechanism changes how entries are registered or how responses are matched; the same Entry.single_register / Entry.register API (see page 4.2) applies in both sync and async tests.
async_mocketize Decoratorasync_mocketize is defined in mocket/decorators/async_mocket.py1-41
The async wrapper function at mocket/decorators/async_mocket.py11-35 does the following:
Mocketizer.factory(...) to construct a configured Mocketizer instance.async with on that instance — this triggers Mocketizer.__aenter__, which calls Mocket.enable().awaits the wrapped test function.Mocketizer.__aexit__ calls Mocket.disable().async_mocketize itself is produced by the same get_mocketize factory used for the sync mocketize, so it accepts the same keyword arguments:
| Parameter | Type | Default | Effect |
|---|---|---|---|
truesocket_recording_dir | str | None | None | Enable VCR-style recording |
strict_mode | bool | False | Raise StrictMocketException on unregistered calls |
strict_mode_allowed | list | None | None | Hosts/ports exempt from strict mode |
Async Decorator Lifecycle
Sources: mocket/decorators/async_mocket.py11-35 mocket/decorators/mocketizer.py78-94
Mocketizer as an Async Context ManagerMocketizer implements __aenter__ and __aexit__ at mocket/decorators/mocketizer.py78-94 Both are thin wrappers around the same synchronous enter() and exit() methods used by the sync __enter__ / __exit__ pair.
Mocketizer async protocol
This means async with Mocketizer(...) is fully equivalent to with Mocketizer(...) — the async variant exists solely so that async with syntax is valid in coroutine functions.
Sources: mocket/decorators/mocketizer.py43-94
asyncio.open_connection Under Mockingasyncio.open_connection internally uses socket.socket, which mocket.inject replaces with MocketSocket while Mocket is active. This means low-level asyncio TCP connections are automatically intercepted without any additional configuration.
The test in tests/test_asyncio.py15-44 demonstrates the record/replay pattern with asyncio.open_connection:
Mocketizer is entered with truesocket_recording_dir set to a temporary directory.asyncio.run(test_asyncio_connection()) is called.asyncio.open_connection (with host, port, family, proto, ssl=None) creates a MocketSocket-backed connection.writer.write() and await writer.drain(), then reads with await reader.readline().Key parameters for asyncio.open_connection compatibility
| Parameter | Recommended value for Mocket |
|---|---|
family | socket.AF_INET |
proto | socket.IPPROTO_TCP |
ssl | None (for plain HTTP) or MocketSSLContext() |
server_hostname | None (for plain HTTP) |
Sources: tests/test_asyncio.py15-44
aiohttp creates ssl.SSLContext instances at import time — before Mocket has replaced ssl.SSLContext with MocketSSLContext. This causes HTTPS mocking to fail unless one of the following approaches is used.
If import aiohttp is executed while Mocket is already in control (i.e., inside a function decorated with @async_mocketize or inside an async with Mocketizer() block), the patched MocketSSLContext will be used.
MocketTCPConnectorMocketTCPConnector is defined in mocket/plugins/aiohttp_connector.py9-18 It subclasses aiohttp's TCPConnector and overrides _get_ssl_context to always return a fresh MocketSSLContext(), bypassing the import-time cached context.
aiohttp + MocketTCPConnector relationship
The test at tests/test_asyncio.py47-69 shows the complete pattern:
@pytest.mark.asyncio marks the test for an asyncio-compatible pytest runner (e.g., pytest-asyncio).@async_mocketize wraps the coroutine, enabling and disabling Mocket around the test body.MocketTCPConnector() is passed as the connector argument to aiohttp.ClientSession.Entry.single_register is called with the target URL and mock body before the HTTP call.Sources: mocket/plugins/aiohttp_connector.py1-18 tests/test_asyncio.py47-69
The README at README.rst29 lists gevent as a supported runtime alongside asyncio. Mocket's compatibility with gevent follows from the same mechanism: gevent monkey-patches the stdlib socket module, and Mocket subsequently patches it again (or cooperates with the already-patched version) when inject.enable() is called. No gevent-specific plugin code exists in the repository; the standard @mocketize / with Mocketizer() usage applies.
Note: Gevent and asyncio cannot be mixed in the same process. Use
@mocketize(sync) for gevent-based code and@async_mocketize(async) for asyncio-based code.
Sources: README.rst29
| Scenario | Recommended entry point |
|---|---|
| Sync test, any HTTP client | @mocketize or with Mocketizer() |
Async test (async def) | @async_mocketize or async with Mocketizer() |
| aiohttp with pre-imported module | @async_mocketize + MocketTCPConnector() |
asyncio.open_connection | with Mocketizer(truesocket_recording_dir=...) or @async_mocketize |
| Gevent-based client | @mocketize or with Mocketizer() |
| pytest-asyncio | @pytest.mark.asyncio + @async_mocketize |
Sources: mocket/decorators/async_mocket.py1-41 mocket/decorators/mocketizer.py1-173 mocket/plugins/aiohttp_connector.py1-18 tests/test_asyncio.py1-69
Refresh this wiki