NestboltNestbolt

@nestbolt/factory

States

Define and apply named entity variations to generate different kinds of test data.

States let you define reusable variations of a factory. Instead of manually overriding fields every time, you define a state once on the factory and apply it by name.

Defining States

Any method on your factory class that returns a Partial<T> can be used as a named state:

import { BaseFactory } from "@nestbolt/factory";
import type { Faker } from "@faker-js/faker";
import { User } from "../entities/user.entity";

export class UserFactory extends BaseFactory<User> {
  get entity() { return User; }

  definition(faker: Faker): Partial<User> {
    return {
      name: faker.person.fullName(),
      email: faker.internet.email(),
      role: "user",
      active: true,
      age: faker.number.int({ min: 18, max: 65 }),
    };
  }

  admin(): Partial<User> {
    return { role: "admin" };
  }

  inactive(): Partial<User> {
    return { active: false };
  }

  senior(): Partial<User> {
    return { age: 65 };
  }
}

Applying Named States

Use the .state() method with a string matching the method name:

// Create an admin user
await factoryService.use(UserFactory).state("admin").create();

// Create an inactive user
await factoryService.use(UserFactory).state("inactive").create();

Combining Multiple States

Chain multiple .state() calls to combine variations. States are applied in order, with later states overriding earlier ones for overlapping fields:

// Create an inactive admin
await factoryService.use(UserFactory)
  .state("admin")
  .state("inactive")
  .create();
// Result: { role: "admin", active: false, ... }

Object States

Pass a plain object to .state() for one-off overrides without defining a method on the factory:

await factoryService.use(UserFactory)
  .state({ role: "moderator", active: true })
  .create();

This is useful for ad-hoc variations that don't warrant a dedicated state method.

Function States

Pass a function to .state() for dynamic values. The function receives the Faker instance:

await factoryService.use(UserFactory)
  .state((faker) => ({
    name: faker.person.firstName() + " Test",
    age: faker.number.int({ min: 18, max: 25 }),
  }))
  .create();

Mixing State Types

All three state types (named, object, function) can be freely combined:

await factoryService.use(UserFactory)
  .state("admin")                                // named state
  .state({ active: true })                       // object state
  .state((faker) => ({ age: faker.number.int({ min: 30, max: 40 }) }))  // function state
  .create();

Non-Existent Named States

If you pass a string that doesn't match any method on the factory, it is silently ignored. This allows for defensive coding when states may or may not be defined:

// If "premium" method doesn't exist on UserFactory, it's ignored
await factoryService.use(UserFactory).state("premium").create();

State Priority

States override definition() values but are themselves overridden by sequence() and override():

definition()  →  state()  →  sequence()  →  override()
  (lowest)                                    (highest)

When multiple states set the same field, the last one wins:

await factoryService.use(UserFactory)
  .state({ role: "admin" })
  .state({ role: "superadmin" })
  .create();
// role is "superadmin"