ProvenPin Install

Seeded test data factories with Fishery and Faker: working versions (tested 2026-10-15)

Build typed test data with Fishery factories backed by seeded @faker-js/faker v10, covering sequences, param overrides, traits, associations, transient params and deterministic output.

passed4 passed · 0 failed2026-10-15 17:10 UTC
Status
passed
Last passed
2026-10-15 17:10 UTC
Tests
4 / 4
Language
TypeScript
Runtime
node v22.23.3
pinned versions4
fishery@2.4.0@faker-js/faker@10.6.0@types/node@26.6.5tsx@4.23.15
run it
npm install --no-audit --no-fund --ignore-scripts=false
npm test
test environment
node v22.23.3
linux 6.17.0-1022-azure
x64
runner: github-actions 38775053802

The code

Exactly the files that passed. Your agent can pull them with get_recipe("faker-fishery-seeded-factories") (install), or see them on GitHub.

package.json
{
  "name": "faker-fishery-seeded-factories",
  "private": true,
  "type": "module",
  "scripts": {
    "test": "node --import tsx --test test/*.test.ts"
  },
  "dependencies": {
    "fishery": "2.4.0",
    "@faker-js/faker": "10.6.0"
  },
  "devDependencies": {
    "@types/node": "26.6.5",
    "tsx": "4.23.15"
  }
}
src/factories.ts
import { Factory } from "fishery";
import { faker } from "@faker-js/faker";

export type User = { id: number; name: string; email: string; role: "member" | "admin"; createdAt: Date };
export type Post = { id: number; title: string; author: User; tags: string[] };

const REF_DATE = new Date("2026-01-01T00:00:00Z");

class UserFactory extends Factory<User, { domain: string }> {
  admin() {
    return this.params({ role: "admin" });
  }
}

export const userFactory = UserFactory.define(({ sequence, transientParams }) => {
  const first = faker.person.firstName();
  const last = faker.person.lastName();
  return {
    id: sequence,
    name: `${first} ${last}`,
    email: faker.internet.email({ firstName: first, lastName: last, provider: transientParams.domain ?? "example.test" }).toLowerCase(),
    role: "member",
    createdAt: faker.date.past({ years: 1, refDate: REF_DATE }),
  };
});

export const postFactory = Factory.define<Post>(({ sequence, associations }) => ({
  id: sequence,
  title: faker.lorem.sentence({ min: 3, max: 6 }),
  author: associations.author ?? userFactory.build(),
  tags: faker.helpers.arrayElements(["ts", "node", "testing", "mcp", "api"], 2),
}));

/** Reset faker and factory sequences so every test starts from the same data. */
export function resetData(seed = 42) {
  faker.seed(seed);
  userFactory.rewindSequence();
  postFactory.rewindSequence();
}
test/factories.test.ts
import { test, beforeEach } from "node:test";
import assert from "node:assert/strict";
import { userFactory, postFactory, resetData } from "../src/factories.ts";

beforeEach(() => resetData());

test("same seed produces identical data", () => {
  const first = userFactory.buildList(3);
  resetData();
  const second = userFactory.buildList(3);
  assert.deepEqual(first, second);
  assert.deepEqual(first.map((u) => u.id), [1, 2, 3]);
});

test("different seeds produce different data", () => {
  const a = userFactory.build();
  resetData(7);
  const b = userFactory.build();
  assert.notEqual(a.name, b.name);
});

test("params, traits and transient params", () => {
  const admin = userFactory.admin().build({ name: "Root User" }, { transient: { domain: "corp.test" } });
  assert.equal(admin.role, "admin");
  assert.equal(admin.name, "Root User");
  assert.match(admin.email, /@corp\.test$/);
  assert.ok(admin.createdAt < new Date("2026-01-01T00:00:00Z"));
});

test("associations reuse a given author", () => {
  const author = userFactory.build({ name: "Ada Lovelace" });
  const posts = postFactory.buildList(2, {}, { associations: { author } });
  // Fishery merges associations into a fresh object, so compare by value.
  assert.deepEqual(posts[0].author, author);
  assert.equal(posts[1].author.name, "Ada Lovelace");
  assert.equal(posts[0].tags.length, 2);
  assert.notEqual(posts[0].id, posts[1].id);
});

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.