Fluent Test Data Builder With Overrides

Create readable fixtures with layered overrides, per-item variation and reset support, keeping tests free of duplicated object literals.

JavascriptCli Tooling

Code

snippet
export function createBuilder(defaults) {
  if (defaults === null || typeof defaults !== "object" || Array.isArray(defaults)) {
    throw new TypeError("defaults must be a plain object");
  }
  const layers = [];
  const build = (overrides = {}) => Object.assign({}, defaults, ...layers, overrides);
  const assertOverrides = (overrides) => {
    if (overrides === null || typeof overrides !== "object" || Array.isArray(overrides)) {
      throw new TypeError("overrides must be a plain object");
    }
  };
  const api = {
    with(overrides) {
      assertOverrides(overrides);
      layers.push(overrides);
      return api;
    },
    reset() {
      layers.length = 0;
      return api;
    },
    build,
    buildList(count, overrides = () => ({})) {
      if (!Number.isInteger(count) || count < 0) {
        throw new RangeError("count must be a non-negative integer");
      }
      if (typeof overrides !== "function") throw new TypeError("overrides must be a function");
      return Array.from({ length: count }, (_, index) => build(overrides(index)));
    },
  };
  return api;
}

const user = createBuilder({ role: "member", active: true });
console.log(user.with({ role: "admin" }).build({ name: "Ada" }));
console.log(user.reset().buildList(2, (index) => ({ id: index + 1 })));