Sparround

Test data strategies

Test data is the number one source of flakiness in automation. The core rule: every test creates its own data, cleans it up, and never touches another test's data.

Four approaches and where each belongs:

  • Static fixture files (JSON) — only for read-only reference data (countries, currencies). Bad for mutable data: parallel tests mutate the same record and break each other.
  • Shared seed data — data preloaded into the environment. Usable READ-ONLY only; the moment someone mutates it, unexplainable failures begin.
  • Factory + API seeding (the main approach) — the object the test needs is built by a factory, created on the backend via API, and deleted in teardown.
  • Creating via the UI — only when the creation flow ITSELF is under test. As setup it is slow and breaks unrelated tests.

The factory pattern — a data-building function with defaults that accepts overrides:

  • buildUser() → a fully valid default user
  • buildUser({ role: 'admin' }) → you state only the difference

This is critical for test readability: the spec shows only the fields relevant to the scenario, and the other 15 stay out of sight. The answer to "why is this test about an admin?" is visible on one line.

Uniqueness for parallel runs — the most common pitfall. Two workers try to create [email protected] at the same moment → one gets a 409 → a random failure. The fix: a unique component in every value — user-${randomUUID()}@test.local. A timestamp alone is NOT ENOUGH: two workers can collide within the same millisecond; UUID, or worker-index + timestamp, is more reliable.

Cleanup belongs in fixture teardown — not in afterEach — because fixture teardown runs even when the test fails. A second line of defence: a nightly cleanup job removing old test data (failed runs always leave litter).

ApproachWhenRisk
Static JSON fixtureImmutable reference data (country, currency lists)Used for mutable data → parallel collisions
Shared seed dataRead-only scenarios, heavy setup (e.g. a large catalogue)One mutation and failures become unexplainable
Factory + API seedingThe default choice — mutable data owned by the testAPI dependency; data piles up if cleanup is forgotten
Creation via the UIOnly when the creation flow itself is under testSlow; a UI change in setup breaks every dependent test

An interview trap: "We turned on parallel execution and random failures started — why?" The expected answer is shared test data (the same user, the same order, the same counter). Give both the diagnosis (run the failures one by one — if they pass, it's an isolation problem) and the fix (unique data + self-cleanup). This single question is one of the best discriminators among middle candidates.

📚 Sources and documentation