Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fixture-factory

npm install @ferrow/fixture-factory

CI

Seeded, deterministic test fixture builder: sequences, pick, factory refs, composable traits, and afterBuild hooks. Strict TypeScript, zero runtime dependencies.

Why

Flaky fixtures make flaky tests. This factory library is deterministic by construction — a mulberry32 PRNG seeded at define() time means the same seed and template always produce the same fixture sequence, so failures are reproducible and snapshot tests stay stable.

Quickstart

import { define, sequence, pick, ref } from "fixture-factory";

const userFactory = define(
  "user",
  {
    id: sequence((i) => `user-${i}`),
    name: pick(["Alice", "Bob", "Cara"]),
    role: "member",
  },
  { seed: 42 }
).trait("admin", { role: "admin" });

const postFactory = define("post", {
  id: sequence((i) => `post-${i}`),
  author: ref(userFactory),
  published: false,
}).trait("published", { published: true });

const post = postFactory.build(undefined, { traits: ["published"] });
const admin = userFactory.build(undefined, { traits: ["admin"] });

API

define(name, template, options?): Factory

options.seed (default 0) seeds the factory's internal PRNG.

Template values

  • Literals — used as-is.
  • sequence(fn)fn(index), where index is a 1-based counter incremented once per build().
  • pick(values) — seeded-random selection from values.
  • ref(factory, overrides?, maxDepth?) — builds a related fixture via another factory. Ref chains deeper than maxDepth (default 3) resolve to null instead of recursing forever.
  • Functions — (ctx) => value, given { rng, sequenceIndex, depth } for custom logic.

Factory.trait(name, partialTemplate)

Registers a named partial template. Composable — pass multiple trait names to build/buildList and they apply in order, each overriding the fields it specifies.

Factory.afterBuild(hook)

Runs hook(built, ctx) after every build; return a value to replace the fixture, or mutate and return undefined.

Factory.build(overrides?, options?) / Factory.buildList(n, overrides?, options?)

options.traits selects which registered traits to apply.

Limits

  • Determinism holds only across separately-defined factories with the same seed and the same sequence of build calls — reordering or adding extra build() calls between two runs will desync their PRNG/sequence state.
  • ref() depth limiting returns null past the limit rather than throwing — check for null in your templates if a ref chain could realistically be circular.
  • No async factories — build functions run synchronously.

Part of the ferrow-toolkit collection · Sponsored by Ferrow

About

Seeded deterministic test fixture builder: sequences, pick, factory refs, composable traits, afterBuild hooks. Zero deps.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages