Summary
The _patch_aiobotocore_response() function is called in ~90+ places across unit test files. Every test that creates a Repository or RateLimiter with moto must manually import and wrap with this function. It should be a single autouse session fixture applied automatically.
Problem
Current Implementation
After #170, the function lives in tests/fixtures/moto.py and is imported everywhere:
# tests/fixtures/moto.py
def _patch_aiobotocore_response():
"""Patch aiobotocore to work with moto's sync responses."""
...
# tests/unit/conftest.py — used in shared fixtures
from tests.fixtures.moto import _patch_aiobotocore_response
@pytest.fixture
async def limiter(mock_dynamodb):
with _patch_aiobotocore_response():
...
# tests/unit/test_repository_builder.py — repeated per test method (~25 times in this file alone)
from tests.unit.conftest import _patch_aiobotocore_response
async def test_builder_basic(self, ...):
with _patch_aiobotocore_response():
repo = await Repository.builder(...).build()
...
async def test_builder_with_namespace(self, ...):
with _patch_aiobotocore_response():
repo = await Repository.builder(...).build()
...
Call sites by file
| File |
_patch_aiobotocore_response calls |
tests/unit/conftest.py |
1 (shared limiter fixture) |
tests/unit/test_limiter.py |
~18 |
tests/unit/test_sync_limiter.py |
~20 |
tests/unit/test_repository.py |
~6 |
tests/unit/test_sync_repository.py |
~6 |
tests/unit/test_repository_builder.py |
~25 |
tests/unit/test_namespace_scoping.py |
1 |
tests/unit/test_namespace_registry.py |
1 |
tests/doctest/conftest.py |
1 |
| Total |
~80-90 |
Issues
- Massive boilerplate: ~90 copies of
from tests.unit.conftest import _patch_aiobotocore_response + with _patch_aiobotocore_response():
- Easy to forget: New tests must remember to apply the patch or get cryptic errors
- Noise in test code: 2 lines of boilerplate per test method obscure the actual test logic
- Re-import pattern: Most test methods import from conftest inside the method body rather than at module level
Why This Matters
Moto returns sync responses but aiobotocore expects async. Without this patch, tests fail with cryptic errors about awaiting non-awaitables deep in boto internals.
Proposed Solution
Convert to an autouse session-scoped fixture in tests/fixtures/moto.py:
# tests/fixtures/moto.py
@pytest.fixture(autouse=True, scope="session")
def patch_aiobotocore_for_moto():
"""Apply moto compatibility patch once for entire test session.
Moto returns botocore.awsrequest.AWSResponse with sync content,
but aiobotocore expects async content. This patch wraps the response
handling to convert sync content to async.
See: https://github.com/aio-libs/aiobotocore/discussions/1300
Autouse ensures the patch is always applied for unit tests.
Session-scoped because the patch is stateless and safe to share.
"""
with _patch_aiobotocore_response():
yield
Then register via conftest.py in directories that use moto:
# tests/unit/conftest.py
from tests.fixtures.moto import patch_aiobotocore_for_moto # noqa: F401 (autouse)
Cleanup
Remove all ~90 instances of:
from tests.unit.conftest import _patch_aiobotocore_response
with _patch_aiobotocore_response():
...
The test body is simply dedented — no other changes needed.
Tasks
Acceptance Criteria
Notes
- Integration/E2E/benchmark tests using LocalStack do NOT need this patch (real async HTTP)
- The
_patch_aiobotocore_response() helper function stays in tests/fixtures/moto.py (the autouse fixture wraps it)
- Some sync test files (
test_sync_limiter.py, test_sync_repository.py) are generated — need to check if the async source files or the generator needs updating
Summary
The
_patch_aiobotocore_response()function is called in ~90+ places across unit test files. Every test that creates a Repository or RateLimiter with moto must manually import and wrap with this function. It should be a single autouse session fixture applied automatically.Problem
Current Implementation
After #170, the function lives in
tests/fixtures/moto.pyand is imported everywhere:Call sites by file
_patch_aiobotocore_responsecallstests/unit/conftest.pylimiterfixture)tests/unit/test_limiter.pytests/unit/test_sync_limiter.pytests/unit/test_repository.pytests/unit/test_sync_repository.pytests/unit/test_repository_builder.pytests/unit/test_namespace_scoping.pytests/unit/test_namespace_registry.pytests/doctest/conftest.pyIssues
from tests.unit.conftest import _patch_aiobotocore_response+with _patch_aiobotocore_response():Why This Matters
Moto returns sync responses but aiobotocore expects async. Without this patch, tests fail with cryptic errors about awaiting non-awaitables deep in boto internals.
Proposed Solution
Convert to an autouse session-scoped fixture in
tests/fixtures/moto.py:Then register via
conftest.pyin directories that use moto:Cleanup
Remove all ~90 instances of:
The test body is simply dedented — no other changes needed.
Tasks
patch_aiobotocore_for_motoautouse session fixture totests/fixtures/moto.pytests/unit/conftest.pyvia importtests/doctest/conftest.pyvia importfrom tests.unit.conftest import _patch_aiobotocore_responseimports from test methodswith _patch_aiobotocore_response():wrappers and dedent test bodieshatch run generate-syncto regenerate sync test files (they contain_patch_aiobotocore_responsecalls too)Acceptance Criteria
_patch_aiobotocore_response()calls remaining in test methodsNotes
_patch_aiobotocore_response()helper function stays intests/fixtures/moto.py(the autouse fixture wraps it)test_sync_limiter.py,test_sync_repository.py) are generated — need to check if the async source files or the generator needs updating