Aiwg generate-factory
Auto-generate test data factories from schemas, types, or models. Use when creating test data infrastructure, setting up fixtures, or reducing test setup boilerplate.
install
source · Clone the upstream repo
git clone https://github.com/jmagly/aiwg
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jmagly/aiwg "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.agents/skills/generate-factory" ~/.claude/skills/jmagly-aiwg-generate-factory && rm -rf "$T"
manifest:
.agents/skills/generate-factory/SKILL.mdsource content
Generate Factory Skill
Purpose
Auto-generate test data factories from database schemas, TypeScript interfaces, or model definitions. Factories produce realistic test data using Faker.js patterns, reducing test setup time by 60%.
Research Foundation
| Pattern | Source | Reference |
|---|---|---|
| Factory Pattern | ThoughtBot | FactoryBot |
| Faker.js | Open Source | fakerjs.dev |
| Test Data Management | ISTQB | CT-TAS Test Automation Strategy |
| Synthetic Data | Tonic.ai | Faker Best Practices |
When This Skill Applies
- User needs to create test data factories
- Setting up test infrastructure for new project
- Existing tests use hard-coded data
- Schema/model changes require test data updates
- Need realistic but deterministic test data
Trigger Phrases
| Natural Language | Action |
|---|---|
| "Generate factory for User model" | Create user factory |
| "Create test data factories" | Generate factories for all models |
| "Add faker to tests" | Integrate faker with existing tests |
| "Make test data realistic" | Convert hard-coded to factory |
| "Generate fixtures from schema" | Schema-aware factory generation |
Factory Concepts
Factory vs Fixture vs Mock
| Type | Purpose | When to Use |
|---|---|---|
| Factory | Generate dynamic test data | When you need many variations |
| Fixture | Static, predefined data | When exact values matter |
| Mock | Fake external dependencies | When isolating units |
Factory Features
// Basic factory const user = userFactory.build(); // { id: 'uuid-1', name: 'John Doe', email: 'john@example.com' } // With overrides const admin = userFactory.build({ role: 'admin' }); // { id: 'uuid-2', name: 'Jane Doe', email: 'jane@example.com', role: 'admin' } // Build multiple const users = userFactory.buildList(5); // Array of 5 users // With traits const inactiveUser = userFactory.build({}, { trait: 'inactive' }); // { id: 'uuid-3', ..., status: 'inactive', deactivatedAt: Date } // With relationships const userWithOrders = userFactory.build({}, { with: ['orders'] }); // { id: 'uuid-4', ..., orders: [{ id: 'order-1', ... }] }
Generation Process
1. Analyze Source
Detect schema/type from:
- TypeScript interfaces
- Prisma schema
- JSON Schema
- Database migrations
- OpenAPI specs
// Input: TypeScript interface interface User { id: string; name: string; email: string; age: number; role: 'admin' | 'user' | 'guest'; createdAt: Date; preferences?: UserPreferences; }
2. Map Types to Faker
const TYPE_MAPPING = { // Primitives 'string': 'faker.string.alphanumeric(10)', 'number': 'faker.number.int({ min: 0, max: 100 })', 'boolean': 'faker.datatype.boolean()', 'Date': 'faker.date.past()', // Named fields (semantic mapping) 'id': 'faker.string.uuid()', 'name': 'faker.person.fullName()', 'email': 'faker.internet.email()', 'phone': 'faker.phone.number()', 'address': 'faker.location.streetAddress()', 'age': 'faker.number.int({ min: 18, max: 80 })', 'createdAt': 'faker.date.past()', 'updatedAt': 'faker.date.recent()', // Enums 'role': 'faker.helpers.arrayElement(["admin", "user", "guest"])', };
3. Generate Factory
// Generated: factories/user.factory.ts import { faker } from '@faker-js/faker'; import type { User } from '../types'; export const userFactory = { /** * Build a single User with optional overrides */ build: (overrides: Partial<User> = {}): User => ({ id: faker.string.uuid(), name: faker.person.fullName(), email: faker.internet.email(), age: faker.number.int({ min: 18, max: 80 }), role: faker.helpers.arrayElement(['admin', 'user', 'guest']), createdAt: faker.date.past(), ...overrides, }), /** * Build multiple Users */ buildList: (count: number, overrides: Partial<User> = {}): User[] => Array.from({ length: count }, () => userFactory.build(overrides)), /** * Traits for common variations */ traits: { admin: { role: 'admin' as const }, inactive: { status: 'inactive', deactivatedAt: faker.date.past(), }, newUser: { createdAt: faker.date.recent(), }, }, /** * Build with trait */ buildWithTrait: (trait: keyof typeof userFactory.traits, overrides: Partial<User> = {}): User => userFactory.build({ ...userFactory.traits[trait], ...overrides }), };
4. Generate Related Factories
For entities with relationships:
// factories/order.factory.ts import { faker } from '@faker-js/faker'; import { userFactory } from './user.factory'; export const orderFactory = { build: (overrides = {}) => ({ id: faker.string.uuid(), userId: faker.string.uuid(), items: [], total: faker.number.float({ min: 10, max: 500, fractionDigits: 2 }), status: faker.helpers.arrayElement(['pending', 'shipped', 'delivered']), createdAt: faker.date.past(), ...overrides, }), /** * Build with related user */ buildWithUser: (overrides = {}) => { const user = userFactory.build(); return { ...orderFactory.build({ userId: user.id, ...overrides }), user, }; }, };
Output Format
## Factory Generation Report **Source**: `src/types/user.ts` **Output**: `test/factories/user.factory.ts` ### Generated Factory ```typescript import { faker } from '@faker-js/faker'; import type { User } from '../../src/types'; export const userFactory = { build: (overrides: Partial<User> = {}): User => ({ id: faker.string.uuid(), name: faker.person.fullName(), email: faker.internet.email(), age: faker.number.int({ min: 18, max: 80 }), role: faker.helpers.arrayElement(['admin', 'user', 'guest']), createdAt: faker.date.past(), preferences: null, ...overrides, }), buildList: (count: number, overrides: Partial<User> = {}): User[] => Array.from({ length: count }, () => userFactory.build(overrides)), traits: { admin: { role: 'admin' as const }, inactive: { status: 'inactive' }, }, };
Field Mappings
| Field | Type | Faker Method |
|---|---|---|
| id | string | |
| name | string | |
| string | | |
| age | number | |
| role | enum | |
| createdAt | Date | |
Usage Examples
// Basic usage const user = userFactory.build(); // With override const admin = userFactory.build({ role: 'admin' }); // Multiple users const users = userFactory.buildList(10); // With trait const inactive = userFactory.build(userFactory.traits.inactive);
Dependencies Added
{ "devDependencies": { "@faker-js/faker": "^8.0.0" } }
## Deterministic Mode For tests requiring reproducible data: ```typescript // Set seed for reproducible data import { faker } from '@faker-js/faker'; beforeEach(() => { faker.seed(12345); // Same data every run }); // Or per-factory export const userFactory = { buildDeterministic: (seed: number, overrides = {}) => { faker.seed(seed); return userFactory.build(overrides); }, };
Batch Generation
Generate factories for all models:
# From Prisma schema npx generate-factory --source prisma/schema.prisma --output test/factories/ # From TypeScript types npx generate-factory --source src/types/ --output test/factories/
Integration Points
- Works with
for test data requirementstdd-enforce - Used by Test Engineer for test creation
- Feeds into integration test setup
- Compatible with database seeders
Script Reference
factory_generator.py
Generate factory from schema:
python scripts/factory_generator.py --source src/types/user.ts
batch_generate.py
Generate all factories:
python scripts/batch_generate.py --source src/types/ --output test/factories/
References
- @$AIWG_ROOT/agentic/code/addons/testing-quality/README.md — Testing quality addon overview
- @$AIWG_ROOT/agentic/code/frameworks/sdlc-complete/README.md — SDLC framework context for test infrastructure
- @$AIWG_ROOT/agentic/code/addons/aiwg-utils/rules/research-before-decision.md — Research-first for schema and type analysis
- @$AIWG_ROOT/docs/cli-reference.md — CLI reference