Factory System
MockMaster’s factory system provides a powerful way to generate realistic, customizable test data using Faker.js.
Overview
Factories let you:
- Generate realistic test data with Faker.js
- Define reusable data templates
- Customize data with overrides
- Create sequences for unique IDs
- Build single objects or lists
Defining Factories
Use defineFactory to create a factory:
import { defineFactory, fake } from '@mockmaster/data'
const userFactory = defineFactory('user', {
id: (ctx) => ctx.sequence('user'),
name: () => fake.person.fullName(),
email: () => fake.internet.email(),
age: () => fake.number.int({ min: 18, max: 80 }),
createdAt: () => fake.date.past()
})Factory Definition
Each field in a factory can be:
- A function that returns a value
- Access to context (
ctx) for sequences - Access to Faker (
fake) for realistic data
Building Objects
Single Object
Use build to create a single object:
import { build } from '@mockmaster/data'
const user = build(userFactory)
console.log(user)
// {
// id: 1,
// name: 'John Doe',
// email: 'john.doe@example.com',
// age: 32,
// createdAt: Date('2023-06-15T10:30:00Z')
// }Multiple Objects
Use buildList to create multiple objects:
import { buildList } from '@mockmaster/data'
const users = buildList(userFactory, 5)
console.log(users)
// [
// { id: 1, name: 'John Doe', ... },
// { id: 2, name: 'Jane Smith', ... },
// { id: 3, name: 'Bob Johnson', ... },
// { id: 4, name: 'Alice Williams', ... },
// { id: 5, name: 'Charlie Brown', ... }
// ]Overriding Values
Override specific fields when building:
const admin = build(userFactory, {
overrides: {
email: 'admin@company.com',
role: 'admin'
}
})
console.log(admin)
// {
// id: 1,
// name: 'John Doe', // Generated by factory
// email: 'admin@company.com', // Overridden
// age: 32, // Generated by factory
// role: 'admin', // Overridden (added new field)
// createdAt: Date(...)
// }Override in Lists
const users = buildList(userFactory, 3, {
overrides: {
company: 'Acme Corp'
}
})
// All users will have company: 'Acme Corp'Sequences
Use sequences for unique, auto-incrementing values:
const userFactory = defineFactory('user', {
id: (ctx) => ctx.sequence('user'), // 1, 2, 3, ...
name: () => fake.person.fullName()
})
const user1 = build(userFactory) // id: 1
const user2 = build(userFactory) // id: 2
const user3 = build(userFactory) // id: 3Named Sequences
Use named sequences to keep different counters:
const userFactory = defineFactory('user', {
id: (ctx) => ctx.sequence('user'), // 1, 2, 3, ...
accountNumber: (ctx) => `ACC-${ctx.sequence('account')}` // ACC-1, ACC-2, ACC-3, ...
})
const user1 = build(userFactory)
// { id: 1, accountNumber: 'ACC-1' }
const user2 = build(userFactory)
// { id: 2, accountNumber: 'ACC-2' }Faker.js Integration
MockMaster includes full Faker.js support via the fake export:
Person Data
import { fake } from '@mockmaster/data'
fake.person.fullName() // 'John Doe'
fake.person.firstName() // 'Jane'
fake.person.lastName() // 'Smith'
fake.person.jobTitle() // 'Software Engineer'
fake.person.sex() // 'female'Internet Data
fake.internet.email() // 'john.doe@example.com'
fake.internet.userName() // 'john_doe123'
fake.internet.password() // 'xK9#mP2$vL5@'
fake.internet.url() // 'https://example.com'
fake.internet.domainName() // 'example.com'Number Data
fake.number.int({ min: 1, max: 100 }) // 42
fake.number.float({ min: 0, max: 1 }) // 0.567Date Data
fake.date.past() // Date in the past
fake.date.future() // Date in the future
fake.date.recent() // Date in last few days
fake.date.between({
from: '2020-01-01',
to: '2024-12-31'
})Address Data
fake.location.city() // 'New York'
fake.location.country() // 'United States'
fake.location.streetAddress() // '123 Main St'
fake.location.zipCode() // '10001'Company Data
fake.company.name() // 'Acme Corp'
fake.company.catchPhrase() // 'Innovative solutions for tomorrow'
fake.company.buzzPhrase() // 'Synergize cross-platform metrics'Commerce Data
fake.commerce.productName() // 'Handcrafted Steel Shoes'
fake.commerce.price() // '49.99'
fake.commerce.department() // 'Electronics'Type-Safe Factories
Use TypeScript interfaces for type safety:
import { defineFactory, build, fake } from '@mockmaster/data'
interface User {
id: number
name: string
email: string
role: 'admin' | 'user' | 'guest'
createdAt: Date
}
const userFactory = defineFactory<User>('user', {
id: (ctx) => ctx.sequence('user'),
name: () => fake.person.fullName(),
email: () => fake.internet.email(),
role: () => fake.helpers.arrayElement(['admin', 'user', 'guest'] as const),
createdAt: () => fake.date.past()
})
// TypeScript knows the return type
const user: User = build(userFactory)Advanced Patterns
Dependent Fields
Fields can depend on other fields:
const userFactory = defineFactory('user', {
firstName: () => fake.person.firstName(),
lastName: () => fake.person.lastName(),
email: (ctx) => {
// Access other fields via ctx
return `${ctx.firstName}.${ctx.lastName}@example.com`.toLowerCase()
}
})
const user = build(userFactory)
// { firstName: 'John', lastName: 'Doe', email: 'john.doe@example.com' }Nested Objects
const addressFactory = defineFactory('address', {
street: () => fake.location.streetAddress(),
city: () => fake.location.city(),
state: () => fake.location.state(),
zipCode: () => fake.location.zipCode()
})
const userFactory = defineFactory('user', {
id: (ctx) => ctx.sequence('user'),
name: () => fake.person.fullName(),
address: () => build(addressFactory)
})
const user = build(userFactory)
// {
// id: 1,
// name: 'John Doe',
// address: {
// street: '123 Main St',
// city: 'New York',
// state: 'NY',
// zipCode: '10001'
// }
// }Conditional Logic
const userFactory = defineFactory('user', {
id: (ctx) => ctx.sequence('user'),
name: () => fake.person.fullName(),
role: () => fake.helpers.arrayElement(['admin', 'user']),
permissions: (ctx) => {
return ctx.role === 'admin'
? ['read', 'write', 'delete']
: ['read']
}
})Real-World Examples
E-commerce Product
const productFactory = defineFactory('product', {
id: (ctx) => ctx.sequence('product'),
name: () => fake.commerce.productName(),
description: () => fake.commerce.productDescription(),
price: () => parseFloat(fake.commerce.price()),
category: () => fake.commerce.department(),
inStock: () => fake.datatype.boolean(),
sku: (ctx) => `SKU-${ctx.id.toString().padStart(6, '0')}`,
createdAt: () => fake.date.past()
})
const product = build(productFactory)Blog Post
const postFactory = defineFactory('post', {
id: (ctx) => ctx.sequence('post'),
title: () => fake.lorem.sentence(),
body: () => fake.lorem.paragraphs(3),
author: () => fake.person.fullName(),
tags: () => fake.helpers.arrayElements(
['javascript', 'typescript', 'react', 'node', 'testing'],
{ min: 1, max: 3 }
),
publishedAt: () => fake.date.past(),
views: () => fake.number.int({ min: 0, max: 10000 })
})
const post = build(postFactory)Order with Items
const orderItemFactory = defineFactory('orderItem', {
id: (ctx) => ctx.sequence('orderItem'),
productName: () => fake.commerce.productName(),
quantity: () => fake.number.int({ min: 1, max: 5 }),
price: () => parseFloat(fake.commerce.price())
})
const orderFactory = defineFactory('order', {
id: (ctx) => ctx.sequence('order'),
orderNumber: (ctx) => `ORD-${ctx.id.toString().padStart(8, '0')}`,
customer: () => fake.person.fullName(),
items: () => buildList(orderItemFactory, fake.number.int({ min: 1, max: 5 })),
total: (ctx) => ctx.items.reduce((sum, item) => sum + item.price * item.quantity, 0),
status: () => fake.helpers.arrayElement(['pending', 'processing', 'shipped', 'delivered']),
createdAt: () => fake.date.past()
})
const order = build(orderFactory)Best Practices
1. Use Meaningful Names
// Good
defineFactory('user', { ... })
defineFactory('product', { ... })
// Bad
defineFactory('u', { ... })
defineFactory('thing', { ... })2. Named Sequences
Always name your sequences:
// Good
(ctx) => ctx.sequence('user')
// Bad
(ctx) => ctx.sequence() // Uses default sequence3. Leverage Faker Formats
Use Faker’s format-aware generators:
{
email: () => fake.internet.email(), // user@example.com
url: () => fake.internet.url(), // https://example.com
phoneNumber: () => fake.phone.number(), // (555) 123-4567
uuid: () => fake.string.uuid(), // '123e4567-e89b-12d3-a456-426614174000'
}4. Consistent Data
For related data, ensure consistency:
{
firstName: () => fake.person.firstName(),
lastName: () => fake.person.lastName(),
// Derived from first and last name
email: (ctx) => `${ctx.firstName}.${ctx.lastName}@example.com`.toLowerCase()
}Next Steps
- Use factories with Scenarios for custom test data
- Combine with OpenAPI Integration for schema-based generation
- See Examples for more factory patterns