| name | test-data-management |
| type | skill |
| description | Manage test data with factories, fixtures, isolation strategies, and cleanup to prevent test pollution. |
| related-rules | ["test-strategy.md","test-data.md"] |
| allowed-tools | Read, Write, Edit, Bash |
Test Data Management Skill
Expertise: Factory functions, database isolation, seed data strategies, test pollution prevention.
Factory Pattern (Python — pytest)
from faker import Faker
from decimal import Decimal
import pytest_asyncio
fake = Faker()
def build_user(**overrides) -> dict:
"""Build a user dict — does NOT write to DB"""
return {
"email": fake.email(domain="example-test.com"),
"name": fake.name(),
"role": "viewer",
"password_hash": "hashed_test_password",
**overrides,
}
def build_order(**overrides) -> dict:
return {
"status": "pending",
"total_amount": Decimal("99.99"),
"currency": "USD",
**overrides,
}
@pytest_asyncio.fixture
async def create_user(db_session):
created = []
async def _create(**overrides):
user = User(**build_user(**overrides))
db_session.add(user)
await db_session.flush()
created.append(user)
return user
yield _create
async def test_user_can_view_own_profile(create_user, client):
user = await create_user(role="viewer")
response = await client.get(f"/users/{user.id}", headers=auth_headers(user))
assert response.status_code == 200
assert response.json()["email"] == user.email
Database Isolation Strategies
Option 1: Transaction rollback (fastest — no cleanup needed)
@pytest_asyncio.fixture
async def db_session(engine):
async with engine.connect() as conn:
transaction = await conn.begin()
session = AsyncSession(bind=conn)
yield session
await transaction.rollback()
await session.close()
Option 2: Truncate tables (compatible with most ORM features)
@pytest_asyncio.fixture(autouse=True)
async def clean_tables(db_session):
yield
await db_session.execute(text("TRUNCATE order_items, orders, users RESTART IDENTITY CASCADE"))
await db_session.commit()
Option 3: Separate test database (for E2E / integration)
services:
db-test:
image: postgres:16
environment:
POSTGRES_DB: myapp_test
tmpfs: [/var/lib/postgresql/data]
Seed Data for E2E Tests
async def seed_standard_dataset(db: AsyncSession):
"""
Creates a deterministic dataset for E2E tests.
All IDs and values are fixed — tests can reference them directly.
"""
admin = User(id=1, email="admin@test.example", role="admin", ...)
user = User(id=2, email="user@test.example", role="viewer", ...)
product_a = Product(id=101, name="Widget A", price=Decimal("29.99"), stock=100)
product_b = Product(id=102, name="Widget B", price=Decimal("49.99"), stock=50)
db.add_all([admin, user, product_a, product_b])
await db.commit()
@pytest.fixture(scope="session", autouse=True)
async def seed(db_session):
await seed_standard_dataset(db_session)
Anti-Patterns to Avoid
orders = []
def test_1():
orders.append(create_order())
def test_2():
assert len(orders) == 0
async def test_order_count_for_new_user(create_user, client):
user = await create_user()
response = await client.get(f"/users/{user.id}/orders")
assert response.json()["count"] == 0
user = build_user(email="john.doe@gmail.com")
user = build_user(email=fake.email(domain="example-test.com"))
Test Data Cleanup Verification
SELECT count(*) FROM users WHERE email LIKE '%example-test.com%';
SELECT count(*) FROM users;