Core ConceptsFactory System

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: 3

Named 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.567

Date 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 sequence

3. 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