Skip to content

Test Data Strategies and Fixtures

Your test suite uses a 500-line seed file copied from production three years ago. Half the tests depend on “John Doe” existing with email “john@test.com” and user ID 1. When someone adds a unique constraint on email, forty tests break. When the schema changes, the seed file requires a manual update that takes half a day. Test data is the foundation of your testing infrastructure — and most teams treat it as an afterthought.

  • Factory pattern implementations that generate consistent, realistic test data on demand
  • Fixture management strategies for different test layers (unit, integration, E2E)
  • Privacy-compliant data generation that mimics production patterns without real PII
  • Database seeding workflows for integration and E2E tests
  • Prompts for generating domain-specific test data factories

Factories replace hardcoded test data with configurable generators that produce fresh, unique data for every test.

@src/lib/db/schema.ts
Generate test data factories for every entity in our database schema:
For each table, create a factory at /tests/factories/{entity}.factory.ts that:
1. Uses @faker-js/faker for realistic data generation
2. Has build() - returns a plain object (for unit tests, no DB)
3. Has create(db) - inserts into the database and returns the record
4. Has buildList(count) - generates an array of objects
5. Has createList(db, count) - inserts multiple records
6. Supports overrides: build({ email: 'specific@test.com' })
7. Supports traits: build('admin'), build('suspended')
8. Handles foreign keys: OrderFactory.create() auto-creates a User if needed
9. Generates UNIQUE values (no collisions even with 1000 records)
Create a factory index at /tests/factories/index.ts that exports all factories.
Follow our Drizzle ORM patterns for database insertions.

Unit tests should never touch the database. Use build() to create plain objects.

// Good: plain objects, no database
const user = UserFactory.build({ role: 'admin' });
const result = authService.checkPermission(user, 'delete_users');
expect(result).toBe(true);

Integration tests need database records but must not leak state between tests.

E2E tests need predictable data that persists across the test run.

“Tests fail because factories generate data that violates business rules.” Your factories need domain knowledge. Add validation to the factory: if a product is “outOfStock”, stock must be 0. If an order is “delivered”, it must have a shipped_at date before delivered_at. Encode business rules in the factory, not just random data.

“Creating test data is slow because of foreign key chains.” Batch insertions instead of creating one record at a time. Pre-create shared reference data (categories, roles) once in a beforeAll hook. Only create entity-specific data per test.

“The seed file keeps getting out of sync with schema changes.” Generate factories from the schema, not by hand. When the schema changes, regenerate: “Read the updated schema and update all factories to match. Add default values for new required columns.”

“We need production data for debugging but cannot use it because of PII.” Use the privacy-compliant generation approach. Create synthetic data that matches production distributions. For specific bug reproduction, anonymize the production record manually: replace the email with a faker email, the name with a faker name, but keep the structural data that reproduces the bug.