← Workflows

阅读中文版本

Test Data Management Workflow

Centralize test data as versioned fixtures and factories, isolate per-test data scopes, and refresh production-shaped data on a schedule.

intermediate~30 mintest-strategytest-dataisolation

Objective

Give every test deterministic, isolated, production-shaped data through a managed system, ending the shared-mutable-fixture chaos.

Environment

  • A test suite currently sharing fixtures or hitting shared databases
  • A fixtures/factories library appropriate to the stack
  • Capacity for a scheduled data refresh job

Steps

1. Inventory current data dependencies

Map which tests read or mutate which shared fixtures and databases, exposing the coupling that causes order dependence.

Expected state: A dependency map shows where tests share mutable state.

2. Build factories for entity creation

Provide factories that create valid entities with overridable fields, so tests declare only what their scenario differs in.

Expected state: Tests construct their own data via factories instead of relying on pre-seeded rows.

3. Scope data per test

Run each test against data it created (unique keys or per-test schemas), deleting or rolling back automatically, eliminating cross-test coupling.

Expected state: Tests pass in any order, and parallel runs do not collide.

4. Version the canonical fixtures

Store the small set of complex real-world fixtures as versioned files with documented provenance, changed through review.

Expected state: Fixture changes are reviewed like code, with provenance documented.

5. Refresh production-shaped sets on schedule

Regenerate the large anonymized or synthetic sets from production patterns on a schedule, keeping cardinality and edge distributions current.

Expected state: Scheduled refresh keeps the production-shaped data drifting no more than a cycle behind.

Expected outcome

Test data that is factory-created, per-test scoped, and production-shaped on a schedule, with the shared-mutable chaos retired.

Verification

Running the suite in randomized order passes repeatedly; parallel execution shows no data collisions; the refresh job's age never exceeds its schedule.

Shared mutable fixtures are order-dependence incarnate

The test suite that depends on a shared seeded database is coupled to itself: tests pass in one order and fail in another, and nobody can say which test owns which rows. Factories plus per-test scoping dissolves the coupling; production-shaped refresh keeps the data honest.

before: shared seed (dev-db, 1.2M rows)
  test A updates user #42 -> test B (later) sees
  changed state -> order-dependent failures

after:
  factory: makeUser({role: 'admin', tier: 2})
  test creates its own user (unique key: test-<uuid>)
  teardown: delete by key (or per-test schema)

  parallel: 4 shards, zero collisions
  order: randomized x 20 runs, all green

fixtures (versioned, reviewed):
  complex-refund-case.json  (provenance: INC-2940)
  seasonal-pricing.json     (prod export 2026-09, masked)

refresh: nightly synth set from prod distributions

Factories encode domain knowledge

A good factory knows what a valid user looks like — which fields are required, which combos are legal — so every test inherits validity by default and states only its difference. That single inversion removes both boilerplate and the invalid-entity bugs that tests used to create accidentally.

Provenance makes fixtures trustworthy

A complex real-world fixture without provenance is folklore: nobody knows if it is still realistic. Recording where it came from (incident number, anonymized export date) makes its eventual retirement a dated decision rather than a mystery.