← Recipes

阅读中文版本

Seed a Test Database with Faker and a Fixture Seed

Generate deterministic fake rows with faker, pin the random seed so every run produces the same data, and reset the database between test suites with a transaction rollback.

~25 mintestingfakerfixtures

Task

Populate a test database with fake but realistic rows that are identical on every run and reset cheaply between tests.

When to use this

Tests share a database and one suite leaves rows that break the next. The team wants each suite to start from a known dataset without a slow truncate between every test.

Prerequisites

  • Node.js 20 LTS with faker installed
  • A database that supports nested transactions or savepoints
  • A fixture seed number committed to the repo

Steps

1. Pin faker to a fixed seed so every run produces the same sequence of values.

import { faker } from "@faker-js/faker";
faker.seed(42);

Expected output: Two runs produce identical rows, so a failing test reproduces the same data every time.

2. Generate a batch of rows with realistic constraints, like unique emails and valid date ranges.

const users = Array.from({ length: 100 }, () => ({
  email: faker.string.uuid() + "@test.example",
  created: faker.date.past(),
}));

Expected output: A hundred rows with unique emails and past created dates, ready to insert.

3. Wrap each test in a transaction that rolls back on teardown so no rows leak between suites.

beforeEach(async () => { tx = await client.query("BEGIN"); });
afterEach(async () => { await client.query("ROLLBACK"); });

Expected output: A test that inserts a user leaves no row for the next test, and the suite runs in a fraction of a second.

Troubleshooting

Problem

A test commits despite the rollback wrapper

Solution

Use a client wrapper that swallows COMMIT and issues ROLLBACK instead, so a stray commit cannot leak state.

Problem

faker versions change the output for the same seed

Solution

Pin faker to an exact version in the lockfile, and regenerate the seed snapshot on a major bump.

Limitations

  • A rollback wrapper does not reset sequences; a serial id can still climb across tests, which matters if a test asserts on a specific id.
  • faker produces realistic but not valid data for every domain; add custom generators for domain-specific fields.

Deterministic fakes

Pin faker to a fixed seed so every run produces the same rows. A failing test reproduces the same data, and a diff in the snapshot means a real change, not a random one.

import { faker } from '@faker-js/faker';
faker.seed(42);
const users = Array.from({ length: 100 }, () => ({
  email: faker.string.uuid() + '@test.example',
  created: faker.date.past(),
}));

Rollback, do not truncate

Wrap each test in a transaction and roll back on teardown. No rows leak between suites, and the reset takes milliseconds instead of a full truncate. Wrap the client so a stray commit cannot escape the rollback.

beforeEach(async () => { tx = await client.query('BEGIN'); });
afterEach(async () => { await client.query('ROLLBACK'); });

Expected outcome

Every run produces the same rows, every test starts from a clean database, and the suite runs fast because the reset is a rollback. Pin faker to an exact version so a bump does not silently change the seed output.