Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Test data generation and management skill covering Faker.js, factory patterns, builders, database seeding, and test data strategies for reliable test suites.
.claude/skills/pramoddutta-test-data-generation/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 14% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 115% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 77% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 233% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 177% | 0% |
You are an expert QA engineer specializing in test data generation and management. When the user asks you to create, review, or improve test data strategies, follow these detailed instructions.
tests/
data/
factories/
user.factory.ts
product.factory.ts
order.factory.ts
builders/
user.builder.ts
order.builder.ts
fixtures/
static-data.json
seeders/
db-seeder.ts
api-seeder.ts
generators/
fake-data.ts
credit-card.ts
utils/
data-cleanup.tsbashnpm install --save-dev @faker-js/faker
typescriptimport { faker } from '@faker-js/faker'; // Generate consistent data with a seed faker.seed(12345); // User data const user = { id: faker.string.uuid(), firstName: faker.person.firstName(), lastName: faker.person.lastName(), email: faker.internet.email(), phone: faker.phone.number(), avatar: faker.image.avatar(), address: { street: faker.location.streetAddress(), city: faker.location.city(), state: faker.location.state(), zip: faker.location.zipCode(), country: faker.location.country(), }, company: faker.company.name(), jobTitle: faker.person.jobTitle(), bio: faker.lorem.paragraph(), createdAt: faker.date.past().toISOString(), }; // Product data const product = { id: faker.string.uuid(), name: faker.commerce.productName(), description: faker.commerce.productDescription(), price: parseFloat(faker.commerce.price({ min: 1, max: 1000 })), category: faker.commerce.department(), sku: faker.string.alphanumeric(10).toUpperCase(), inStock: faker.datatype.boolean(), rating: faker.number.float({ min: 1, max: 5, fractionDigits: 1 }), imageUrl: faker.image.url(), }; // Financial data const transaction = { id: faker.string.uuid(), amount: parseFloat(faker.finance.amount({ min: 10, max: 5000 })), currency: faker.finance.currencyCode(), accountNumber: faker.finance.accountNumber(), routingNumber: faker.finance.routingNumber(), transactionType: faker.helpers.arrayElement(['credit', 'debit', 'transfer']), date: faker.date.recent({ days: 30 }).toISOString(), status: faker.helpers.arrayElement(['pending', 'completed', 'failed', 'reversed']), };
typescriptimport { faker } from '@faker-js/faker'; import { fakerDE } from '@faker-js/faker'; import { fakerJA } from '@faker-js/faker'; // German locale const germanUser = { name: fakerDE.person.fullName(), address: fakerDE.location.streetAddress(), phone: fakerDE.phone.number(), }; // Japanese locale const japaneseUser = { name: fakerJA.person.fullName(), address: fakerJA.location.streetAddress(), };
typescript// factories/user.factory.ts import { faker } from '@faker-js/faker'; export interface User { id: string; email: string; firstName: string; lastName: string; role: 'admin' | 'user' | 'viewer'; isActive: boolean; createdAt: string; } export interface CreateUserInput { email: string; firstName: string; lastName: string; password: string; role?: 'admin' | 'user' | 'viewer'; } export class UserFactory { static create(overrides: Partial<User> = {}): User { return { id: faker.string.uuid(), email: faker.internet.email(), firstName: faker.person.firstName(), lastName: faker.person.lastName(), role: 'user', isActive: true, createdAt: faker.date.past().toISOString(), ...overrides, }; } static createMany(count: number, overrides: Partial<User> = {}): User[] { return Array.from({ length: count }, () => this.create(overrides)); } static createInput(overrides: Partial<CreateUserInput> = {}): CreateUserInput { return { email: faker.internet.email(), firstName: faker.person.firstName(), lastName: faker.person.lastName(), password: faker.internet.password({ length: 12, memorable: false }), role: 'user', ...overrides, }; } static createAdmin(overrides: Partial<User> = {}): User { return this.create({ role: 'admin', ...overrides }); } static createInactive(overrides: Partial<User> = {}): User { return this.create({ isActive: false, ...overrides }); } }
typescriptimport { test, expect } from '@playwright/test'; import { UserFactory } from '../data/factories/user.factory'; test('should create a new user', async ({ request }) => { const userData = UserFactory.createInput(); const response = await request.post('/api/users', { data: userData }); expect(response.status()).toBe(201); const body = await response.json(); expect(body.email).toBe(userData.email); expect(body.firstName).toBe(userData.firstName); }); test('should list users with pagination', async ({ request }) => { // Create multiple users const users = UserFactory.createMany(15); for (const user of users) { await request.post('/api/users', { data: UserFactory.createInput({ email: user.email, firstName: user.firstName, }), }); } const response = await request.get('/api/users?page=1&pageSize=10'); const body = await response.json(); expect(body.data.length).toBe(10); expect(body.total).toBeGreaterThanOrEqual(15); });
typescript// builders/order.builder.ts import { faker } from '@faker-js/faker'; export interface OrderItem { productId: string; name: string; quantity: number; price: number; } export interface Order { id: string; customerId: string; items: OrderItem[]; status: 'pending' | 'confirmed' | 'shipped' | 'delivered' | 'cancelled'; shippingAddress: { street: string; city: string; state: string; zip: string; country: string; }; totalAmount: number; createdAt: string; } export class OrderBuilder { private order: Order; constructor() { this.order = { id: faker.string.uuid(), customerId: faker.string.uuid(), items: [], status: 'pending', shippingAddress: { street: faker.location.streetAddress(), city: faker.location.city(), state: faker.location.state(), zip: faker.location.zipCode(), country: 'US', }, totalAmount: 0, createdAt: new Date().toISOString(), }; } withCustomer(customerId: string): this { this.order.customerId = customerId; return this; } withItem(item?: Partial<OrderItem>): this { const newItem: OrderItem = { productId: item?.productId ?? faker.string.uuid(), name: item?.name ?? faker.commerce.productName(), quantity: item?.quantity ?? faker.number.int({ min: 1, max: 5 }), price: item?.price ?? parseFloat(faker.commerce.price({ min: 5, max: 200 })), }; this.order.items.push(newItem); this.order.totalAmount = this.order.items.reduce( (sum, i) => sum + i.price * i.quantity, 0 ); return this; } withItems(count: number): this { for (let i = 0; i < count; i++) { this.withItem(); } return this; } withStatus(status: Order['status']): this { this.order.status = status; return this; } withShippingTo(country: string): this { this.order.shippingAddress.country = country; return this; } cancelled(): this { return this.withStatus('cancelled'); } delivered(): this { return this.withStatus('delivered'); } build(): Order { if (this.order.items.length === 0) { this.withItem(); // Add at least one item } return { ...this.order }; } } // Usage in tests const order = new OrderBuilder() .withCustomer('customer-123') .withItem({ name: 'Widget', price: 29.99, quantity: 2 }) .withItem({ name: 'Gadget', price: 49.99, quantity: 1 }) .withShippingTo('US') .build();
pythonfrom faker import Faker fake = Faker() Faker.seed(42) # For reproducibility user = { "id": fake.uuid4(), "email": fake.email(), "first_name": fake.first_name(), "last_name": fake.last_name(), "phone": fake.phone_number(), "address": fake.address(), "company": fake.company(), "created_at": fake.date_time_this_year().isoformat(), }
pythonimport factory from faker import Faker from myapp.models import User, Order fake = Faker() class UserFactory(factory.Factory): class Meta: model = User id = factory.LazyFunction(fake.uuid4) email = factory.LazyFunction(fake.email) first_name = factory.LazyFunction(fake.first_name) last_name = factory.LazyFunction(fake.last_name) role = "user" is_active = True class Params: admin = factory.Trait(role="admin") inactive = factory.Trait(is_active=False) # Usage user = UserFactory() admin = UserFactory(admin=True) inactive_users = UserFactory.create_batch(5, inactive=True)
javaimport com.github.javafaker.Faker; import java.util.Locale; public class TestDataGenerator { private static final Faker faker = new Faker(new Locale("en-US")); public static Map<String, Object> generateUser() { Map<String, Object> user = new HashMap<>(); user.put("email", faker.internet().emailAddress()); user.put("firstName", faker.name().firstName()); user.put("lastName", faker.name().lastName()); user.put("phone", faker.phoneNumber().cellPhone()); user.put("address", faker.address().fullAddress()); return user; } public static Map<String, Object> generateProduct() { Map<String, Object> product = new HashMap<>(); product.put("name", faker.commerce().productName()); product.put("price", Double.parseDouble(faker.commerce().price())); product.put("category", faker.commerce().department()); product.put("description", faker.lorem().paragraph()); return product; } }
typescript// seeders/db-seeder.ts import { UserFactory } from '../factories/user.factory'; import { ProductFactory } from '../factories/product.factory'; import { OrderBuilder } from '../builders/order.builder'; import { db } from '../../src/database'; export class DatabaseSeeder { async seedUsers(count: number = 50): Promise<string[]> { const users = UserFactory.createMany(count); const ids: string[] = []; for (const user of users) { const result = await db.users.create({ data: user }); ids.push(result.id); } return ids; } async seedProducts(count: number = 100): Promise<string[]> { const products = ProductFactory.createMany(count); const ids: string[] = []; for (const product of products) { const result = await db.products.create({ data: product }); ids.push(result.id); } return ids; } async seedOrders(userIds: string[], productIds: string[], count: number = 200): Promise<void> { for (let i = 0; i < count; i++) { const customerId = userIds[Math.floor(Math.random() * userIds.length)]; const order = new OrderBuilder() .withCustomer(customerId) .withItems(Math.floor(Math.random() * 5) + 1) .withStatus(['pending', 'confirmed', 'shipped', 'delivered'][Math.floor(Math.random() * 4)] as any) .build(); await db.orders.create({ data: order }); } } async seedAll(): Promise<void> { const userIds = await this.seedUsers(); const productIds = await this.seedProducts(); await this.seedOrders(userIds, productIds); console.log('Database seeded successfully'); } async cleanup(): Promise<void> { await db.orders.deleteMany({}); await db.products.deleteMany({}); await db.users.deleteMany({}); console.log('Database cleaned up'); } }
Generate data within each test. Best for unit and integration tests.
typescripttest('should validate email format', () => { const validEmail = faker.internet.email(); const result = validateEmail(validEmail); expect(result).toBe(true); });
Static data loaded from JSON files. Best for snapshot testing and deterministic scenarios.
json{ "validUser": { "email": "test@example.com", "password": "ValidPass123!", "name": "Test User" }, "invalidEmails": ["not-email", "@missing.com", "spaces here@bad.com"] }
Deterministic random data using a fixed seed. Best for reproducible randomized tests.
typescriptbeforeEach(() => { faker.seed(Date.now()); // Different seed each run // OR faker.seed(42); // Same data every run });
Create test data via API calls before tests run. Best for E2E tests.
typescripttest.beforeAll(async ({ request }) => { const user = UserFactory.createInput(); await request.post('/api/users', { data: user }); });
"user1@test.com" causes conflicts in parallel tests.beforeAll with shared data leads to coupled tests.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 27,924 | 28,373 | +2% | 1 | 1 | 0% | 4,446 | 9,094 | +105% | 0 | 0 | — |
case-02 | fail→pass | 39,932 | 31,163 | -22% | 1 | 1 | 0% | 8,258 | 9,385 | +14% | 0 | 0 | — |
case-03 | pass→pass | 22,668 | 29,799 | +31% | 1 | 1 | 0% | 3,115 | 8,719 | +180% | 0 | 0 | — |
case-04 | pass→fail | 18,782 | 21,816 | +16% | 1 | 1 | 0% | 2,404 | 7,717 | +221% | 0 | 0 | — |
case-05 | pass→pass | 21,910 | 23,574 | +8% | 1 | 1 | 0% | 3,042 | 7,838 | +158% | 0 | 0 | — |
case-06 | fail→fail | 19,750 | 18,473 | -6% | 1 | 1 | 0% | 2,912 | 6,607 | +127% | 0 | 0 | — |
case-07 | pass→pass | 21,094 | 22,221 | +5% | 1 | 1 | 0% | 2,662 | 7,725 | +190% | 0 | 0 | — |
case-08 | fail→pass | 22,894 | 21,568 | -6% | 1 | 1 | 0% | 3,517 | 7,562 | +115% | 0 | 0 | — |
case-09 | fail→pass | 21,441 | 18,855 | -12% | 1 | 1 | 0% | 4,144 | 7,346 | +77% | 0 | 0 | — |
case-10 | pass→pass | 25,132 | 21,769 | -13% | 1 | 1 | 0% | 3,411 | 8,293 | +143% | 0 | 0 | — |
case-11 | fail→pass | 18,576 | 19,031 | +2% | 1 | 1 | 0% | 2,142 | 7,123 | +233% | 0 | 0 | — |
case-12 | fail→fail | 21,974 | 18,507 | -16% | 1 | 1 | 0% | 3,537 | 7,746 | +119% | 0 | 0 | — |
case-13 | pass→pass | 20,785 | 25,043 | +20% | 1 | 1 | 0% | 2,728 | 8,045 | +195% | 0 | 0 | — |
case-14 | pass→pass | 21,316 | 18,946 | -11% | 1 | 1 | 0% | 2,584 | 6,598 | +155% | 0 | 0 | — |
case-15 | fail→fail | 24,572 | 24,063 | -2% | 1 | 1 | 0% | 3,179 | 7,743 | +144% | 0 | 0 | — |
case-16 | fail→pass | 24,283 | 25,323 | +4% | 1 | 1 | 0% | 2,845 | 7,870 | +177% | 0 | 0 | — |
case-17 | fail→pass | 22,100 | 24,006 | +9% | 1 | 1 | 0% | 2,739 | 7,641 | +179% | 0 | 0 | — |
case-18 | fail→fail | 22,977 | 26,368 | +15% | 1 | 1 | 0% | 3,277 | 8,343 | +155% | 0 | 0 | — |
case-19 | fail→pass | 26,664 | 26,417 | -1% | 1 | 1 | 0% | 3,836 | 8,091 | +111% | 0 | 0 | — |
case-20 | pass→pass | 14,930 | 17,075 | +14% | 1 | 1 | 0% | 1,430 | 6,265 | +338% | 0 | 0 | — |
case-21 | pass→pass | 21,956 | 23,379 | +6% | 1 | 1 | 0% | 2,422 | 7,282 | +201% | 0 | 0 | — |
case-22 | pass→pass | 26,740 | 21,945 | -18% | 1 | 1 | 0% | 3,433 | 7,109 | +107% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +27 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.