Testing¶
aiodynamodb ships a mock_dynamodb() async context manager that starts an in-memory mock of AWS DynamoDB, creates tables for your models, and yields a ready-to-use DynamoDB client — no real AWS account needed.
Install the testing extra¶
Or with uv:
This installs aiomoto and moto[dynamodb].
mock_dynamodb¶
from aiodynamodb.testing import mock_dynamodb
async with mock_dynamodb(User, Order) as db:
await db.put(User(user_id="u1", name="Alice"))
user = await db.get(User, hash_key="u1")
assert user is not None
What it does¶
- Patches AWS environment variables with fake credentials (
AWS_ACCESS_KEY_ID=testing, etc.) - Starts the
aiomotomock for DynamoDB - Creates tables for all provided model classes by calling
db.create_table(model)for each - Yields the
DynamoDBclient - Tears down the mock on exit
Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
*models |
type[DynamoModel] |
— | Model classes whose tables should be created |
patch_env |
bool |
True |
Patch AWS env vars with fake credentials. Set False if you manage credentials yourself. |
pytest setup¶
Install pytest-asyncio:
Configure asyncio_mode = "auto" in pyproject.toml (already done in this project):
Writing tests¶
Basic test¶
import pytest
from aiodynamodb import DynamoModel, table
from aiodynamodb.testing import mock_dynamodb
@table("users", hash_key="user_id")
class User(DynamoModel):
user_id: str
name: str
async def test_put_and_get():
async with mock_dynamodb(User) as db:
await db.put(User(user_id="u1", name="Alice"))
result = await db.get(User, hash_key="u1")
assert result is not None
assert result.name == "Alice"
Using a fixture¶
import pytest
from aiodynamodb import DynamoDB
from aiodynamodb.testing import mock_dynamodb
@pytest.fixture
async def db():
async with mock_dynamodb(User, Order) as client:
yield client
async def test_something(db: DynamoDB):
await db.put(User(user_id="u1", name="Alice"))
...
Testing condition expressions¶
import pytest
from boto3.dynamodb.conditions import Attr
async def test_conditional_put_fails():
async with mock_dynamodb(User) as db:
ex = await db.exceptions()
await db.put(User(user_id="u1", name="Alice"))
with pytest.raises(ex.ConditionalCheckFailedException):
await db.put(
User(user_id="u1", name="Bob"),
condition_expression=Attr("user_id").not_exists(),
)
Testing queries¶
async def test_query_pagination():
async with mock_dynamodb(Order) as db:
for i in range(5):
await db.put(Order(order_id="o1", created_at=f"2026-01-0{i+1}", total=i * 10))
from boto3.dynamodb.conditions import Key
all_items = []
async for page in db.query(
Order,
key_condition_expression=Key("order_id").eq("o1"),
):
all_items.extend(page.items)
assert len(all_items) == 5
What aiomoto mocks¶
aiomoto intercepts all aioboto3 network calls and emulates DynamoDB in memory. This includes:
put_item,get_item,delete_item,update_itemquery,scantransact_get_items,transact_write_itemsbatch_get_item,batch_write_itemcreate_table,delete_table
All DynamoDB behavior (conditions, projections, indexes, pagination) is emulated — but it is not 100% identical to the real service. For full fidelity, use localstack or a real DynamoDB table in integration tests.