@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"