Forger Help

Concepts

Forger is built around a simple idea: a test should declare what data matters and forge the rest. This topic explains the mental model; the machinery is described in How it works.

Declaration over preparation

Traditional test data is prepared: the developer writes every field by hand, even though only one or two fields are relevant to the assertion. The reader cannot tell the important values from the filler.

Forger inverts it. The starting point is always a fully generated object; the test then declares exceptions:

const invoice = Forger.createWith<Invoice>() .with(i => i.total = -1) // the only value this test is about .result();

Anything not mentioned is random filler — and visibly so.

The moving parts

Part

Role

Forger.create<T>()

The public entry point: returns a forged T.

Forger.createWith<T>()

The same, plus the ability to pin properties via .with()/.result().

SpoofSettings

Tuning for numbers, strings, dates, and arrays (see Settings).

The transformer

Compile-time step that reads the type of T and bakes it into the call site.

Factories

Runtime step that turns the baked type description into actual random values.

Type information flows one way

TypeScript types do not exist at runtime — after compilation, Student is gone. Forger solves this by capturing the type at compile time and serializing it into a small description tree (a ForgerElement) that travels into the compiled call as an extra argument. At runtime the factories walk that tree and produce values.

For a user of the library this has one important consequence: the transformer must be part of the build. Without it there is no type information, and create<T>() returns undefined.

Deterministic shape, random values

Forger guarantees the shape of what it produces — properties, nesting, array lengths, enum membership — but the concrete values are random on every call. Tests should therefore assert on shape and constraints (typeof, toBeGreaterThan, array length, enum membership), never on concrete generated values. When a test needs a concrete value, that value should be pinned with createWith.

Where to go from here

08 September 2026