
Test Data Generation
- 445 installs
- 305 repo stars
- Updated March 4, 2026
- aj-geddes/useful-ai-prompts
test-data-generation is an AI prompt skill that creates realistic synthetic datasets for unit, integration, and QA runs for developers blocked by thin fixtures, privacy rules, or missing edge-case diversity.
About
test-data-generation is a skill from aj-geddes/useful-ai-prompts that produces realistic synthetic datasets when production copies are unavailable or non-compliant. It covers unit, integration, and QA scenarios where fixtures are too thin, privacy policies block real records, or edge cases need diverse data at scale. The skill guides schema-aware generation—valid formats, boundary values, relational consistency, and volume profiles—so tests exercise realistic paths without leaking PII. Reach for test-data-generation when bootstrapping a new service schema, expanding coverage for rare states, or unblocking CI that currently mocks only happy-path rows. Output includes seed files, factory definitions, or bulk JSON/CSV artifacts teams can wire into test runners and staging environments.
- Realistic synthetic records
- Edge-case and boundary coverage
- Privacy-safe alternatives to prod dumps
- Seeding for integration suites
- Scalable volume generation
Test Data Generation by the numbers
- 445 all-time installs (skills.sh)
- Ranked #630 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/aj-geddes/useful-ai-prompts --skill test-data-generationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 445 |
|---|---|
| repo stars | ★ 305 |
| Last updated | March 4, 2026 |
| Repository | aj-geddes/useful-ai-prompts ↗ |
How do you generate realistic test data without prod copies?
Create realistic synthetic datasets for unit, integration, and QA runs when fixtures are thin, privacy blocks prod copies, or edge cases need diverse records at scale.
Who is it for?
Developers blocked by privacy rules or thin fixtures who need diverse, schema-valid synthetic records at unit, integration, or QA scale.
Skip if: Developers who already have sufficient anonymized production snapshots and only need one trivial mock object.
When should I use this skill?
Test suites lack realistic fixtures, privacy blocks prod data copies, or edge-case diversity is required before running integration or QA pipelines.
What you get
Synthetic datasets, factory definitions, and seed files with diverse edge cases for test suites.
- Synthetic test datasets
- Factory or seed file definitions
Files
Test Data Generation
Table of Contents
Overview
Test data generation creates realistic, consistent, and maintainable test data for automated testing. Well-designed test data reduces test brittleness, improves readability, and makes it easier to create diverse test scenarios.
When to Use
- Creating fixtures for integration tests
- Generating fake data for development databases
- Building test data with complex relationships
- Creating realistic user inputs for testing
- Seeding test databases
- Generating edge cases and boundary values
- Building reusable test data factories
Quick Start
Minimal working example:
// tests/factories/userFactory.js
const { faker } = require("@faker-js/faker");
class UserFactory {
static build(overrides = {}) {
return {
id: faker.string.uuid(),
email: faker.internet.email(),
firstName: faker.person.firstName(),
lastName: faker.person.lastName(),
age: faker.number.int({ min: 18, max: 80 }),
phone: faker.phone.number(),
address: {
street: faker.location.streetAddress(),
city: faker.location.city(),
state: faker.location.state(),
zip: faker.location.zipCode(),
country: "USA",
},
role: "user",
isActive: true,
createdAt: faker.date.past(),
...overrides,
};
}
// ... (see reference guides for full implementation)Reference Guides
Detailed implementations in the references/ directory:
| Guide | Contents |
|---|---|
| Factory Pattern for Test Data | Factory Pattern for Test Data |
| Builder Pattern for Complex Objects | Builder Pattern for Complex Objects |
| Fixtures for Integration Tests | Fixtures for Integration Tests |
| Realistic Data Generation | Realistic Data Generation |
Best Practices
✅ DO
- Use faker libraries for realistic data
- Create reusable factories for common objects
- Make factories flexible with overrides
- Generate unique values where needed (emails, IDs)
- Use builders for complex object construction
- Create fixtures for integration test setup
- Generate edge cases (empty strings, nulls, boundaries)
- Keep test data deterministic when possible
❌ DON'T
- Hardcode test data in multiple places
- Use production data in tests
- Generate truly random data for reproducible tests
- Create overly complex factory hierarchies
- Ignore data relationships and constraints
- Generate massive datasets for simple tests
- Forget to clean up generated data
- Use the same test data for all tests
Builder Pattern for Complex Objects
Builder Pattern for Complex Objects
// tests/builders/OrderBuilder.ts
import { faker } from "@faker-js/faker";
export class OrderBuilder {
private order: Partial<Order> = {
id: faker.string.uuid(),
status: "pending",
items: [],
total: 0,
createdAt: new Date(),
};
withId(id: string): this {
this.order.id = id;
return this;
}
withStatus(status: OrderStatus): this {
this.order.status = status;
return this;
}
withUser(user: User): this {
this.order.userId = user.id;
this.order.user = user;
return this;
}
withItems(items: OrderItem[]): this {
this.order.items = items;
this.order.total = items.reduce(
(sum, item) => sum + item.price * item.quantity,
0,
);
return this;
}
addItem(product: Product, quantity: number = 1): this {
const item: OrderItem = {
productId: product.id,
product,
quantity,
price: product.price,
subtotal: product.price * quantity,
};
this.order.items = [...(this.order.items || []), item];
this.order.total = (this.order.total || 0) + item.subtotal;
return this;
}
withShippingAddress(address: Address): this {
this.order.shippingAddress = address;
return this;
}
asPaid(): this {
this.order.status = "paid";
this.order.paidAt = new Date();
return this;
}
asShipped(): this {
this.order.status = "shipped";
this.order.shippedAt = new Date();
return this;
}
build(): Order {
return this.order as Order;
}
}
// Usage in tests
describe("Order Processing", () => {
it("should calculate total correctly", () => {
const product1 = ProductBuilder.aProduct().withPrice(10.0).build();
const product2 = ProductBuilder.aProduct().withPrice(25.0).build();
const order = new OrderBuilder()
.withUser(UserBuilder.aUser().build())
.addItem(product1, 2) // $20
.addItem(product2, 1) // $25
.build();
expect(order.total).toBe(45.0);
expect(order.items).toHaveLength(2);
});
it("should process paid orders", () => {
const order = new OrderBuilder()
.withUser(UserBuilder.aUser().build())
.addItem(ProductBuilder.aProduct().build())
.asPaid()
.build();
expect(order.status).toBe("paid");
expect(order.paidAt).toBeDefined();
});
});Factory Pattern for Test Data
Factory Pattern for Test Data
JavaScript/Jest with Factory Functions
// tests/factories/userFactory.js
const { faker } = require("@faker-js/faker");
class UserFactory {
static build(overrides = {}) {
return {
id: faker.string.uuid(),
email: faker.internet.email(),
firstName: faker.person.firstName(),
lastName: faker.person.lastName(),
age: faker.number.int({ min: 18, max: 80 }),
phone: faker.phone.number(),
address: {
street: faker.location.streetAddress(),
city: faker.location.city(),
state: faker.location.state(),
zip: faker.location.zipCode(),
country: "USA",
},
role: "user",
isActive: true,
createdAt: faker.date.past(),
...overrides,
};
}
static buildMany(count, overrides = {}) {
return Array.from({ length: count }, () => this.build(overrides));
}
static buildAdmin(overrides = {}) {
return this.build({
role: "admin",
permissions: ["read", "write", "delete"],
...overrides,
});
}
static buildInactive(overrides = {}) {
return this.build({
isActive: false,
deactivatedAt: faker.date.recent(),
...overrides,
});
}
}
// tests/user.test.js
describe("User Service", () => {
test("should create user with valid data", () => {
const userData = UserFactory.build();
const user = userService.create(userData);
expect(user.email).toBe(userData.email);
expect(user.isActive).toBe(true);
});
test("should handle admin users differently", () => {
const admin = UserFactory.buildAdmin();
expect(admin.role).toBe("admin");
expect(admin.permissions).toContain("delete");
});
test("should process multiple users", () => {
const users = UserFactory.buildMany(5);
expect(users).toHaveLength(5);
expect(new Set(users.map((u) => u.email)).size).toBe(5); // All unique
});
});Python with Factory Boy
# tests/factories.py
import factory
from factory.faker import Faker
from datetime import datetime, timedelta
from app.models import User, Order, Product
class UserFactory(factory.Factory):
class Meta:
model = User
id = factory.Sequence(lambda n: n)
email = Faker('email')
first_name = Faker('first_name')
last_name = Faker('last_name')
username = factory.LazyAttribute(
lambda obj: f"{obj.first_name.lower()}.{obj.last_name.lower()}"
)
age = Faker('random_int', min=18, max=80)
phone = Faker('phone_number')
is_active = True
role = 'user'
created_at = Faker('date_time_this_year')
class Params:
# Traits for different user types
admin = factory.Trait(
role='admin',
permissions=['read', 'write', 'delete']
)
inactive = factory.Trait(
is_active=False,
deactivated_at=factory.LazyFunction(datetime.now)
)
premium = factory.Trait(
subscription='premium',
subscription_end=factory.LazyFunction(
lambda: datetime.now() + timedelta(days=365)
)
)
class ProductFactory(factory.Factory):
class Meta:
model = Product
id = factory.Sequence(lambda n: n)
name = Faker('commerce_product_name')
description = Faker('text', max_nb_chars=200)
price = Faker('pydecimal', left_digits=3, right_digits=2, positive=True)
sku = factory.LazyAttribute(
lambda obj: f"SKU-{obj.id:06d}"
)
stock = Faker('random_int', min=0, max=100)
category = Faker('random_element', elements=['electronics', 'clothing', 'books'])
is_available = factory.LazyAttribute(lambda obj: obj.stock > 0)
class OrderFactory(factory.Factory):
class Meta:
model = Order
id = factory.Sequence(lambda n: n)
user = factory.SubFactory(UserFactory)
status = 'pending'
total = Faker('pydecimal', left_digits=4, right_digits=2, positive=True)
created_at = Faker('date_time_this_month')
@factory.post_generation
def products(self, create, extracted, **kwargs):
"""Add products to order after creation."""
if not create:
return
if extracted:
for product in extracted:
self.products.add(product)
else:
# Add 1-3 random products by default
count = kwargs.get('count', 3)
self.products.add(*ProductFactory.build_batch(count))
# tests/test_orders.py
import pytest
from tests.factories import UserFactory, OrderFactory, ProductFactory
def test_create_order_with_products():
"""Test order creation with specific products."""
products = ProductFactory.build_batch(3)
order = OrderFactory.build(products=products)
assert order.user is not None
assert len(order.products) == 3
assert order.status == 'pending'
def test_admin_user_permissions():
"""Test admin user has correct permissions."""
admin = UserFactory.build(admin=True)
assert admin.role == 'admin'
assert 'delete' in admin.permissions
def test_inactive_user():
"""Test inactive user properties."""
user = UserFactory.build(inactive=True)
assert not user.is_active
assert user.deactivated_at is not None
def test_bulk_user_creation():
"""Test creating multiple users at once."""
users = UserFactory.build_batch(10, role='user')
assert len(users) == 10
assert all(u.role == 'user' for u in users)
# All emails should be unique
assert len(set(u.email for u in users)) == 10Fixtures for Integration Tests
Fixtures for Integration Tests
Jest/TypeScript with Database Fixtures
// tests/fixtures/database.ts
import { PrismaClient } from "@prisma/client";
import { UserFactory, ProductFactory, OrderFactory } from "./factories";
export class DatabaseFixtures {
constructor(private prisma: PrismaClient) {}
async seed() {
// Create users
const users = await Promise.all(
UserFactory.buildMany(10).map((userData) =>
this.prisma.user.create({ data: userData }),
),
);
// Create products
const products = await Promise.all(
ProductFactory.buildMany(20).map((productData) =>
this.prisma.product.create({ data: productData }),
),
);
// Create orders
const orders = await Promise.all(
OrderFactory.buildMany(15).map((orderData) =>
this.prisma.order.create({
data: {
...orderData,
userId: users[Math.floor(Math.random() * users.length)].id,
items: {
create: products.slice(0, 3).map((product) => ({
productId: product.id,
quantity: Math.floor(Math.random() * 3) + 1,
price: product.price,
})),
},
},
}),
),
);
return { users, products, orders };
}
async clear() {
await this.prisma.orderItem.deleteMany();
await this.prisma.order.deleteMany();
await this.prisma.product.deleteMany();
await this.prisma.user.deleteMany();
}
}
// tests/setup.ts
import { PrismaClient } from "@prisma/client";
import { DatabaseFixtures } from "./fixtures/database";
const prisma = new PrismaClient();
const fixtures = new DatabaseFixtures(prisma);
beforeAll(async () => {
await fixtures.clear();
await fixtures.seed();
});
afterAll(async () => {
await fixtures.clear();
await prisma.$disconnect();
});pytest Fixtures
# tests/conftest.py
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from tests.factories import UserFactory, ProductFactory, OrderFactory
@pytest.fixture(scope='session')
def engine():
"""Create database engine."""
return create_engine('sqlite:///:memory:')
@pytest.fixture(scope='session')
def tables(engine):
"""Create all tables."""
Base.metadata.create_all(engine)
yield
Base.metadata.drop_all(engine)
@pytest.fixture
def db_session(engine, tables):
"""Create database session for each test."""
Session = sessionmaker(bind=engine)
session = Session()
yield session
session.rollback()
session.close()
@pytest.fixture
def sample_users(db_session):
"""Create sample users for testing."""
users = UserFactory.build_batch(5)
db_session.add_all(users)
db_session.commit()
return users
@pytest.fixture
def sample_products(db_session):
"""Create sample products for testing."""
products = ProductFactory.build_batch(10)
db_session.add_all(products)
db_session.commit()
return products
@pytest.fixture
def admin_user(db_session):
"""Create an admin user."""
admin = UserFactory.build(admin=True)
db_session.add(admin)
db_session.commit()
return admin
@pytest.fixture
def order_with_items(db_session, sample_users, sample_products):
"""Create an order with items."""
order = OrderFactory.build(
user=sample_users[0],
products=sample_products[:3]
)
db_session.add(order)
db_session.commit()
return order
# Usage in tests
def test_user_orders(order_with_items):
"""Test user has correct orders."""
user = order_with_items.user
assert len(user.orders) == 1
assert user.orders[0].id == order_with_items.idRealistic Data Generation
Realistic Data Generation
// tests/helpers/dataGenerator.js
const { faker } = require("@faker-js/faker");
class DataGenerator {
static generateCreditCard() {
return {
number: faker.finance.creditCardNumber("#### #### #### ####"),
cvv: faker.finance.creditCardCVV(),
expiry: faker.date.future().toISOString().slice(0, 7), // YYYY-MM
type: faker.helpers.arrayElement(["visa", "mastercard", "amex"]),
};
}
static generateAddress() {
return {
street: faker.location.streetAddress(),
city: faker.location.city(),
state: faker.location.state(),
zip: faker.location.zipCode(),
country: faker.location.country(),
coordinates: {
lat: parseFloat(faker.location.latitude()),
lng: parseFloat(faker.location.longitude()),
},
};
}
static generateDateRange(days = 30) {
const endDate = new Date();
const startDate = new Date();
startDate.setDate(startDate.getDate() - days);
return { startDate, endDate };
}
static generateTimeSeries(count, interval = "day") {
const data = [];
const now = new Date();
for (let i = count - 1; i >= 0; i--) {
const date = new Date(now);
if (interval === "day") date.setDate(date.getDate() - i);
if (interval === "hour") date.setHours(date.getHours() - i);
data.push({
timestamp: date,
value: faker.number.float({ min: 0, max: 100, precision: 0.01 }),
});
}
return data;
}
static generateRealisticEmail(firstName, lastName, domain = "example.com") {
const patterns = [
`${firstName}.${lastName}`,
`${firstName}${lastName}`,
`${firstName.charAt(0)}${lastName}`,
`${firstName}_${lastName}`,
];
const pattern = faker.helpers.arrayElement(patterns);
return `${pattern.toLowerCase()}@${domain}`;
}
}
module.exports = { DataGenerator };#!/bin/bash
# scaffold-tests.sh - Generate test file scaffolding
# Usage: ./scaffold-tests.sh <source_file> [--framework jest|pytest|mocha]
set -euo pipefail
SOURCE_FILE="${{1:?Usage: $0 <source_file> [--framework jest|pytest|mocha]}}"
FRAMEWORK="${{2:-jest}}"
echo "Scaffolding tests for: $SOURCE_FILE (framework: $FRAMEWORK)"
# TODO: Implement test scaffolding logic
# - Parse source file for exported functions/classes
# - Generate test stubs for each export
# - Include setup/teardown boilerplate
# - Add common assertion patterns
echo "Test scaffolding complete."
// Test Template
// TODO: Customize for your testing framework and project
describe('ModuleName', () => {
// Setup
beforeEach(() => {
// TODO: Add test setup
});
afterEach(() => {
// TODO: Add cleanup
});
describe('functionName', () => {
it('should handle the happy path', () => {
// TODO: Add assertion
});
it('should handle edge cases', () => {
// TODO: Add edge case tests
});
it('should handle errors gracefully', () => {
// TODO: Add error handling tests
});
});
});
Related skills
How it compares
Pick test-data-generation over test-automation-framework when the blocker is dataset realism and privacy-safe fixtures rather than overall suite architecture.
FAQ
When should developers use test-data-generation?
test-data-generation fits when fixtures are thin, privacy blocks production copies, or edge cases need diverse records at scale. The skill targets unit, integration, and QA tiers with schema-valid synthetic output instead of copying real user data.
What artifacts does test-data-generation produce?
test-data-generation yields synthetic datasets, factory definitions, and seed files—often JSON or CSV—with boundary values and relational consistency. Teams wire these into test runners, staging databases, or CI jobs without exposing production PII.