You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The test suite creates ~122 CloudFormation stacks per full run (one per test function or class), each taking 2-10 seconds. This makes integration and E2E tests painfully slow despite tests needing only data isolation, not infrastructure isolation. Using shared session-scoped stacks with namespace-based test isolation eliminates most stack creation overhead.
Problem
Current State
Layer
Tests
Stacks created
Stack scope
Time per stack
Unit
many
0 (moto)
—
—
Integration
85
~63
mostly function
~2-10s (LocalStack)
E2E
75
~29
mixed function/class
~2-10s (LocalStack)
Benchmark
81
~30
function (LocalStack ones)
~2-10s (LocalStack)
~122 CloudFormation stacks per full test run. Each stack creation includes CloudFormation waiter polling + optional Lambda ESM stabilization (~45s on real AWS). The majority (103/122) are function-scoped, meaning each test creates and tears down its own stack.
Root Cause
Tests only need data isolation (separate entities, buckets, configs), not infrastructure isolation (separate DynamoDB tables, Lambdas, IAM roles). Since v0.10.0, namespaces provide exactly this — every DynamoDB key is prefixed with {namespace_id}/, giving complete data isolation within a shared table.
Additional Issues
Fixture duplication: integration/conftest.py and e2e/conftest.py are ~93% identical (~120 lines duplicated)
Cross-module imports: benchmark/conftest.py imports from both, creating fragile dependencies
No single source of truth: Hard to know which conftest is "canonical"
Proposed Solution
Shared Stacks (Session-Scoped)
Only 3-4 distinct stack configurations are needed across all tests:
Fixture
Config
Used by
shared_minimal_stack
No aggregator, no alarms
Most integration, some e2e
shared_aggregator_stack
Aggregator enabled
Aggregator tests, some benchmarks
shared_full_stack
Aggregator + alarms
Full e2e tests
Each creates the stack once per pytest session and deletes it at teardown.
Per-Test Namespace (Function-Scoped)
@pytest.fixtureasyncdeftest_namespace(shared_minimal_stack):
"""Register a unique namespace for this test, return a scoped Repository."""ns_name=f"test-{uuid.uuid4().hex[:8]}"repo=await (
Repository.builder(shared_minimal_stack.name, "us-east-1",
endpoint_url=shared_minimal_stack.endpoint_url)
.namespace(ns_name)
.build()
)
yieldrepo# No cleanup needed — session teardown deletes the whole stack
Namespace registration cost: 2 WCU per namespace (~10ms on LocalStack), cached after first call.
Unit tests keep using create_table() + manual namespace registration (moto doesn't support CloudFormation). They benefit from the tests/fixtures/ extraction (eliminating duplication, adding namespace awareness).
Expected Performance Improvement
Layer
Before
After
Savings
Integration (85 tests)
~63 stacks × ~5s ≈ 5 min
1 stack + 85 namespaces × ~10ms ≈ 6s
~98%
E2E (75 tests)
~29 stacks × ~5s ≈ 2.5 min
2-3 stacks + 75 namespaces × ~10ms ≈ 11s
~95%
Benchmark (81 tests)
~30 stacks × ~5s ≈ 2.5 min
1-2 stacks shared ≈ 10s
~93%
Edge Cases
Tests needing distinct stack configs (e.g., specific Lambda timeout): still create their own stack, but these are rare
Summary
The test suite creates ~122 CloudFormation stacks per full run (one per test function or class), each taking 2-10 seconds. This makes integration and E2E tests painfully slow despite tests needing only data isolation, not infrastructure isolation. Using shared session-scoped stacks with namespace-based test isolation eliminates most stack creation overhead.
Problem
Current State
~122 CloudFormation stacks per full test run. Each stack creation includes CloudFormation waiter polling + optional Lambda ESM stabilization (~45s on real AWS). The majority (103/122) are function-scoped, meaning each test creates and tears down its own stack.
Root Cause
Tests only need data isolation (separate entities, buckets, configs), not infrastructure isolation (separate DynamoDB tables, Lambdas, IAM roles). Since v0.10.0, namespaces provide exactly this — every DynamoDB key is prefixed with
{namespace_id}/, giving complete data isolation within a shared table.Additional Issues
integration/conftest.pyande2e/conftest.pyare ~93% identical (~120 lines duplicated)benchmark/conftest.pyimports from both, creating fragile dependenciesProposed Solution
Shared Stacks (Session-Scoped)
Only 3-4 distinct stack configurations are needed across all tests:
shared_minimal_stackshared_aggregator_stackshared_full_stackEach creates the stack once per pytest session and deletes it at teardown.
Per-Test Namespace (Function-Scoped)
Namespace registration cost: 2 WCU per namespace (~10ms on LocalStack), cached after first call.
Package Structure
Extract all shared fixtures to
tests/fixtures/:Key design:
stacks.pymanages session-scoped shared stacks.repositories.pycreates function-scoped namespace-isolated repos within those shared stacks.Impact on conftest.py Files
unit/conftest.py(102 lines)tests.fixtures.motointegration/conftest.py(152 lines)e2e/conftest.py(373 lines)benchmark/conftest.py(334 lines)tests.fixturesdirectlydoctest/conftest.py(547 lines)_namespace_idhackingmake_moto_repo()from fixturesMoto Fixtures (Unchanged Strategy)
Unit tests keep using
create_table()+ manual namespace registration (moto doesn't support CloudFormation). They benefit from thetests/fixtures/extraction (eliminating duplication, adding namespace awareness).Expected Performance Improvement
Edge Cases
shared_aggregator_stack— all namespaces' stream records are processed by the same Lambda (which already extractsnamespace_idfrom PKs via ✨ Aggregator: namespace extraction from stream records #367)Tasks
tests/fixtures/__init__.pypackagetests/fixtures/moto.pytests/fixtures/names.pywithunique_nameandunique_namespacefactoriestests/fixtures/stacks.pywith session-scoped shared stack fixturestests/fixtures/repositories.pywith namespace-scoped repo/limiter factoriestests/fixtures/aws_clients.pywith boto3 client factoriestests/fixtures/polling.pywith E2E polling helperstests/fixtures/capacity.pywith benchmark CapacityCountertests/fixtures/doctest_helpers.pywith doctest mock classestests/unit/conftest.pyto import from fixturestests/integration/conftest.pyto use shared stack + namespace fixturestests/e2e/conftest.pyto use shared stack + namespace fixturestests/benchmark/conftest.pyto import from fixtures directlytests/doctest/conftest.pyto use fixture helpersCLAUDE.mdTesting section with fixture package documentation.claude/rules/testing.mdfixture organization guidelinesAcceptance Criteria
tests/fixtures/is properly documented in CLAUDE.mdDocumentation Updates
CLAUDE.md Changes
Add to Testing section: