| name | test-data-generator |
| description | Test data generation expertise covering Faker libraries across languages, factory patterns (Factory Boy, Fishery), seed data management, data anonymization techniques, synthetic data generation, constraint satisfaction, maintaining data relationships, database seeding strategies, and snapshot testing for data validation.
Use when the user asks about test data generator, test data generator best practices, or needs guidance on test data generator implementation.
Do NOT use when the user needs a different specialized skill or is asking about an unrelated technology domain.
|
| license | Apache-2.0 |
| metadata | {"author":"foundry-skills","version":"1.0.0","tags":"testing best-practices guide","category":"testing-quality","subcategory":"test-automation","depends":"","disclaimer":"none","difficulty":"intermediate"} |
Test Data Generator
Core Philosophy
Good tests require good data. Test data should be realistic enough to catch real bugs, minimal enough to keep tests fast and readable, and deterministic enough to make failures reproducible. The goal is to eliminate the "test data problem" -- the hours spent manually crafting data that exercises all code paths.
Faker Libraries
Python: Faker
from faker import Faker
import random
fake = Faker()
Faker.seed(42)
name = fake.name()
email = fake.email()
address = fake.address()
phone = fake.phone_number()
text = fake.text(max_nb_chars=200)
date = fake.date_between('-1y', 'today')
uuid = fake.uuid4()
url = fake.url()
ip = fake.ipv4()
credit_card = fake.credit_card_number()
company = fake.company()
job = fake.job()
ssn = fake.ssn()
return self.random_element(self.categories)
def product_price(self, min_price=0.99, max_price=999.99):
return round(random.uniform(min_price, max_price), 2)
fake.add_provider(EcommerceProvider)
fake.product_name()
fake.product_price()
JavaScript: @faker-js/faker
import { faker } from '@faker-js/faker';
faker.seed(42);
function generateUser(supersedes = {}) {
const firstName = faker.person.firstName();
const lastName = faker.person.lastName();
return {
id: faker.string.uuid(),
firstName,
lastName,
email: faker.internet.email({ firstName, lastName }),
avatar: faker.image.avatar(),
phone: faker.phone.number(),
address: {
street: faker.location.streetAddress(),
city: faker.location.city(),
state: faker.location.state({ abbreviated: true }),
zip: faker.location.zipCode(),
country: 'US',
# ... (condensed) ...
const users = .({ : }, ());
orders = users.(
.(
{ : faker..({ : , : }) },
(user.)
)
);
Factory Patterns
Python: Factory Boy
import factory
from factory import fuzzy
from datetime import datetime, timezone, timedelta
from myapp.models import User, Order, OrderItem, Product
class UserFactory(factory.Factory):
class Meta:
model = User
id = factory.Sequence(lambda n: n + 1)
name = factory.Faker('name')
email = factory.LazyAttribute(
lambda o: f"{o.name.lower().replace(' ', '.')}@test.com"
)
tier = factory.Iterator(['free', 'basic', 'premium'])
is_active = True
created_at = factory.LazyFunction(lambda: datetime.now(timezone.utc))
class Params:
inactive = factory.Trait(
is_active=False,
order = OrderFactory(user=user, total=200.00)
discount = calculate_discount(order)
assert discount == 30.00
def test_inactive_user_cannot_order():
user = UserFactory(inactive=True)
with pytest.raises(InactiveUserError):
create_order(user, items=[ProductFactory()])
JavaScript: Fishery
import { Factory } from 'fishery';
import { faker } from '@faker-js/faker';
const userFactory = Factory.define<User>(({ sequence, params, transientParams }) => ({
id: sequence,
name: faker.person.fullName(),
email: params.email || faker.internet.email(),
role: params.role || 'user',
isActive: true,
createdAt: faker.date.past(),
}));
const productFactory = Factory.define<Product>(({ sequence }) => ({
id: `prod_${sequence}`,
name: faker.commerce.productName(),
price: parseFloat(faker.commerce.price({ min: 5, max: 500 })),
category: faker.helpers.arrayElement([, , ]),
: faker..({ : , : }),
}));
orderFactory = .<>( {
# ... (condensed) ...
user = userFactory.({ : });
premiumUser = userFactory.({ : });
products = productFactory.();
order = orderFactory.({}, {
: { : premiumUser }
});
Seed Data Management
Database Seeding Script
import json
from pathlib import Path
class DatabaseSeeder:
def __init__(self, db_session):
self.db = db_session
def seed_all(self):
"""Seed all reference and test data in dependency order."""
self.seed_categories()
self.seed_products()
self.seed_users()
self.seed_orders()
self.db.commit()
def seed_categories(self):
categories = [
Category(id=1, name="Electronics", slug="electronics"),
Category(id=2, name="Clothing", slug="clothing"),
Category(id=3, name="Home & Garden", slug="home-garden"),
Category(id=4, name="Books", slug="books"),
order = OrderFactory(
user=user,
items=[
OrderItemFactory(product=random.choice(products))
for _ in range(random.randint(1, 3))
]
)
.db.add(order)
Data Anonymization
class DataAnonymizer:
"""Anonymize production data for use in testing/staging."""
def __init__(self):
self.fake = Faker()
Faker.seed(0)
self._email_map = {}
def anonymize_user(self, user: dict) -> dict:
original_email = user['email']
if original_email not in self._email_map:
self._email_map[original_email] = self.fake.email()
return {
**user,
'name': self.fake.name(),
'email': self._email_map[original_email],
'phone': self.fake.phone_number(),
'ssn': None,
'address': {
'street': self.fake.street_address(),
shift_days = hash(date_str) % 365
return (dt + timedelta(days=shift_days)).isoformat()
anonymizer = DataAnonymizer()
production_users = load_production_export('users.json')
anonymized = [anonymizer.anonymize_user(u) u production_users]
save_to_file(, anonymized)
Constraint Satisfaction
Generating Valid Related Data
class ConstrainedDataGenerator:
"""Generate data that satisfies business rules and foreign key constraints."""
def generate_valid_order(self) -> Order:
"""Order must have: valid user, in-stock products, correct totals."""
user = UserFactory(is_active=True)
products = [
ProductFactory(stock=fuzzy.FuzzyInteger(10, 100).fuzz())
for _ in range(random.randint(1, 4))
]
items = []
for product in products:
quantity = random.randint(1, min(5, product.stock))
items.append(OrderItem(
product_id=product.id,
quantity=quantity,
unit_price=product.price
))
for _ in range(random.randint(5, 50)):
cursor += timedelta(hours=random.randint(1, 72))
if cursor > now:
break
event_type = random.choice(['login', 'view_product', 'add_to_cart', 'purchase'])
events.append(Event(user_id=user_id, type=event_type, timestamp=cursor))
return events
Snapshot Testing
def test_user_serialization(snapshot):
user = UserFactory(
name="Alice Smith",
email="alice@example.com",
tier="premium",
created_at=datetime(2025, 1, 15, 10, 30, 0, tzinfo=timezone.utc)
)
result = UserSerializer().serialize(user)
assert result == snapshot
test('user API response matches snapshot', () => {
const user = userFactory.build({
name: 'Alice Smith',
email: 'alice@example.com',
});
const serialized = serializeUser(user);
expect(serialized).toMatchSnapshot();
});
test('error message format', () => {
const error = validateEmail('not-an-email');
expect(error).toMatchInlineSnapshot(`"Invalid email format: not-an-email"`);
});
Best Practices
- Always seed Faker for reproducibility: Same seed produces same data across runs
- Use factories over raw constructors: Factories express intent and handle defaults
- Generate minimal data: Only create what the test needs
- Use traits for variations:
UserFactory(premium=True) over manual field setting
- Keep seed data in version control: JSON/YAML files alongside code
- Anonymize production data carefully: Preserve relationships and data distributions
- Validate generated data: Run business rule validation on generated datasets
- Document test accounts: Known credentials for E2E testing in a secure location
When to Use
Use this skill when:
- Designing or implementing test data generator solutions
- Reviewing or improving existing test data generator approaches
- Making architectural or implementation decisions about test data generator
- Learning test data generator patterns and best practices
- Troubleshooting test data generator-related issues
Do NOT use this skill when:
- The question is about a fundamentally different technology domain
- A more specific sibling skill covers the exact topic needed
- The user needs a complete hands-on tutorial rather than expert guidance
Output Format
# Test Data Generator Analysis
## Context Assessment
[Situation summary and constraints]
## Recommended Approach
[Primary recommendation with rationale]
## Implementation Steps
1. [Step with specific details]
2. [Step with specific details]
3. [Step with specific details]
## Trade-offs and Considerations
- [Key trade-off 1]
- [Key trade-off 2]
## Next Steps
- [Immediate action item]
- [Follow-up action item]
Example
Input: "Help me implement test data generator for a medium-scale production application"
Output: A structured analysis covering current state assessment, recommended test data generator approach with specific patterns, implementation roadmap with milestones, and risk mitigation strategies tailored to the application scale and constraints.
Edge Cases
- Legacy system integration: When test data generator must coexist with legacy approaches, provide a gradual migration path rather than a complete rewrite
- Scale mismatch: When the solution complexity exceeds the project scale, recommend a simpler approach and note when to revisit
- Team skill gaps: When the team lacks experience with the recommended approach, include learning resources and simpler alternatives
- Conflicting requirements: When constraints conflict (e.g., performance vs. maintainability), explicitly state the trade-off and recommend based on stated priorities