Testland
Browse all skills & agents

test-data-patterns

Pure reference catalog of the cross-language object-construction patterns for test data - Test Data Builder (Pryce/Freeman), Factory (with traits and associations), Object Mother, Fixture composition (per-test / per-describe / shared), Snapshot (defers to `golden-file-conventions` for the operational details), and Production-Data Anonymisation. Distinct from per-language data wrappers (`factory-bot-data` Ruby, `faker-data` JS, `mimesis-data` Python, `bogus-data` .NET) which document tool-specific configuration; this catalog is the architecture-tier reference for choosing **which pattern** before reaching for the tool. Use when choosing a test-data construction pattern for a new suite, or auditing an existing suite whose fixtures have drifted into shared mutable state.

Install with skills.sh (any agent)

npx skills add testland/qa --skill test-data-patterns
View source

test-data-patterns

Overview

This skill is a pure reference - no execution steps. It is the catalog cited when auditing a test framework's data-construction approach. It complements factory-bot-data (Ruby), faker-data (JS), mimesis-data (Python), bogus-data (.NET), synthetic-pii-generator (cross-language), and golden-file-conventions (snapshot pattern). Those skills document the tools; this skill documents the patterns.

When to use

  • Designing test-data construction strategy for a new framework - pick the right pattern before reaching for the tool.
  • Auditing an existing framework where test data is the bottleneck (every test re-creates the world; tests run for minutes; data drifts between tests).
  • Migrating between tools (Ruby's FactoryBot → Python's factory_boy, JS's Fishery → Python's Polyfactory) - the pattern stays; the tool changes.
  • Onboarding engineers - point them at the canonical pattern citation.

Do not use this skill to:

  • Configure a specific tool - that's the per-language skill for that tool.
  • Generate negative / boundary / parameterized test data - that's negative-test-generator, boundary-value-generator, pairwise-test-case-generator.
  • Author an E2E seed fixture for the whole suite - that's seed-data-curator.

Pattern 1 - Test Data Builder

Canonical source: Nat Pryce and Steve Freeman, the Test Data Builder pattern (see References).

Definition: A Test Data Builder is a class with chainable methods (.withName("Alice").withOrgId(42).build()) that constructs domain objects step-by-step. Every field has a sensible default; the test overrides only the fields it cares about.

Concrete implementation (TypeScript, runnable):

type User = { role: string; org: { plan: string } };

class UserBuilder {
  private user: User = { role: "standard", org: { plan: "free" } };
  withRole(role: string) { this.user.role = role; return this; }
  withOrg(org: User["org"]) { this.user.org = org; return this; }
  build(): User { return { ...this.user }; }
}
const aUser = () => new UserBuilder();

// Test overrides only the fields it cares about:
const admin = aUser().withRole("admin").build();

When to use Test Data Builder

  • Domain objects have 5+ fields and most tests only care about 1-2.
  • The team values explicit "what this test cares about" in the test body.
  • The language has chainable / fluent API support (most modern languages).

Anti-patterns

Anti-patternWhy it fails
Builders with .set<Field>(value) for every field (no defaults)Loses the pattern's benefit; every test specifies every field
Builders that mutate the object in place rather than returning a new oneTest cross-coupling: builders shared between tests leak state
Builders that perform side effects (build() writes to DB)Mixes two concerns; the test cannot tell whether it's constructing or persisting
Builders for objects with 2 fieldsOverhead exceeds benefit; use a struct literal

Pattern 2 - Factory (with traits and associations)

Canonical source: Thoughtbot's factory_bot (Ruby) is the cross-language reference implementation; the pattern adapts the Gang of Four Factory Method for test data.

Definition: A Factory is a registered, named template for creating an object. Traits are named modifiers (:admin, :disabled, :premium) that compose with the base template. Associations express relationships (user.org, org.plan).

Example (Ruby FactoryBot, cited as the canonical implementation):

factory :user do
  name { Faker::Name.name }
  email { Faker::Internet.email }

  trait :admin do
    role { :admin }
  end

  trait :with_org do
    association :org, factory: :org
  end
end

# Test usage:
admin_user = create(:user, :admin, :with_org)

Cross-language equivalents (canonical per-language tool; URLs in References):

  • Ruby: factory_bot - the origin.
  • Python: factory_boy - direct port.
  • JS/TS: fishery, @faker-js/faker (lower-level).
  • .NET: Bogus - Faker-style with factory affordances.
  • Java: data-faker + handwritten factory or test-data-builder-style libraries.

When to use Factory

  • The project uses a database (factories handle FK relationships via associations).
  • The team is on Ruby / Python / JS-TS where mature factory libraries exist.
  • Domain objects have many variants (admin / disabled / premium / legacy) - traits express each.

Anti-patterns

Anti-patternWhy it fails
Factory definitions that hard-code IDs (id: 1)Tests collide in parallel; factories must let the DB / Faker assign
Traits that overlap silently (:admin and :premium both set role)Order-dependent behaviour; trait composition becomes unpredictable
Factories that persist by default (create is the only mode)Slow tests, unnecessary DB writes. Builders should expose build / attributes / create strategies (see factory-bot-data)
One mega-factory for the entire domainBecomes a god-object; every test pulls a fully-populated graph

Pattern 3 - Object Mother

Canonical source: Martin Fowler, Object Mother (see References). Predates Test Data Builder; superseded by it for most use cases but still useful for stable canonical objects.

Definition: A central class (the "Mother") exposes named methods that return fully-constructed canonical test objects: ObjectMother.standardUser(), ObjectMother.adminUser(), ObjectMother.userInOrgWithFiveMembers().

Fowler's framing: "An Object Mother is a class that contains methods that create well-known objects for use in tests." Useful when the team has a small, stable set of canonical fixtures.

When to use Object Mother

  • The domain has a small, stable set of canonical objects ("the test admin", "the seed catalogue").
  • The team is on a language without first-class factory libraries (older Java, C++, legacy stacks).
  • The Test Data Builder pattern is overkill for the small number of cases.

Anti-patterns

Anti-patternWhy it fails
Object Mother that grows to 50+ methodsBecomes a god-class; engineers can't find the right factory
Methods that return shared mutable instancesTests cross-couple through the Mother's return values
Mixing Mother with Builder (some objects via Mother, some via Builder)Inconsistent test idiom; vocabulary drift
Mother methods with implicit dependencies (adminUser() requires seedOrgs() to have run)Implicit ordering creates flakiness

Pattern 4 - Fixture composition

Canonical source: Gerard Meszaros, xUnit Test Patterns - the seminal reference for Fresh Fixture, Shared Fixture, Implicit Setup, Delegated Setup, and Setup Decorator patterns; it defines the four-phase test pattern (setup / exercise / verify / teardown). See References.

Definition: Fixture composition is the pattern of building per-test state from reusable fragments. The three flavours:

FlavourWhen
Fresh FixtureEach test creates its own state from scratch. Most isolated; slowest.
Shared FixtureMultiple tests share one initialised state. Fastest; brittle to test ordering.
Persistent Fresh FixtureFresh state per test, but persisted in a transaction that rolls back at teardown. The pragmatic middle ground.

Fowler on the trade-off (Eradicating Non-Determinism in Tests (opens in new window)): "I prefer the former [Fresh Fixture], as it's often easier - and in particular easier to find the source of a problem." But: "rebuilding the database each time can add a lot of time to test runs, so that argues for switching to a clean-up strategy."

When to use Fresh Fixture (default)

  • Unit / integration tests with fast setup.
  • Tests that mutate state (Shared Fixture would leak across tests).
  • Anything parallel-executed.

When to use Shared Fixture

  • E2E tests where setup is genuinely expensive (multi-service stack, large seed data).
  • Tests that only read the fixture (no mutation).
  • The fixture is documented as immutable and the team enforces it.

Anti-patterns

Anti-patternWhy it fails
Shared Fixture that some tests quietly mutateCross-test coupling; failures depend on test order
Fresh Fixture for a 30-minute E2E seedTest suite time becomes infeasible; team starts skipping tests
Multiple fixture flavours in the same suite without explicit conventionEngineers can't tell what to write; bugs creep in
Fixture inheritance hierarchies >2 levels deepDepth-3+ chains break unpredictably

Pattern 5 - Snapshot / golden-file

Canonical source: Jest snapshot testing is the cross-language reference for the test-code side; Michael Feathers' Working Effectively with Legacy Code coined "characterisation tests", the legacy-code-tier version of snapshot testing. See References.

Definition: A snapshot test compares the current output of code under test to a previously-saved canonical output ("the golden file"). When the test runs, it serialises the output, compares against the file, fails if they differ. Engineers explicitly approve a new golden file when the change is intentional.

This skill's role: Snapshot is a recurring concept in test-data conversation, but the operational details (file naming, sanitisation of timestamps / IDs / PII, per-OS variants, review workflow) are documented in detail by golden-file-conventions. Reach for that skill for the operational catalog; this section is the pattern's catalog entry only.

When to use Snapshot

  • Output is structured and large (HTML render, JSON response, CLI output).
  • Manual assertions would be tedious or wrong (50 fields to check).
  • Changes to output are infrequent and require explicit approval anyway.

When NOT to use Snapshot

  • Output is non-deterministic (timestamps, UUIDs, locale, PII) - sanitise first or skip the snapshot.
  • Output changes frequently - the team will rubber-stamp the snapshot update and lose the test's value.
  • The behaviour you care about is one field - write an explicit assertion.

Pattern 6 - Production-Data Anonymisation

Canonical source: ISO/IEC 25024 (data quality) and GDPR / CCPA requirements; synthetic-pii-generator is the companion PII-synthesis skill. Tooling vendors listed in References.

Definition: Anonymisation is the technique of using production data (or production-shaped data) for testing after removing or masking personally-identifiable information (PII), commercially-sensitive data, and any field that would breach privacy / compliance if leaked to a test environment.

Why this is a pattern, not just a tool concern: The pattern dictates that no production data enters a test environment without anonymisation - even if the test environment is "internal only." Cross-environment data leakage is a dominant security failure mode in test-data management (2025 Verizon DBIR; see References).

The three anonymisation flavours

FlavourDefinitionWhen
MaskingReplace sensitive fields with deterministic placeholders (X*** for surnames; static fake date)Production-shape preserved; field-level reversible if needed
SynthesisGenerate fake data that statistically resembles production (length distributions, locale mix)No mapping back to production; safest
TokenisationReplace sensitive values with tokens that map back via a secured lookupWhen the test environment needs to round-trip data to production (rare in QA)

Anti-patterns

Anti-patternWhy it fails
Copying production DB to staging "for realism"Cross-environment data leakage; GDPR / CCPA / HIPAA breach surface
Anonymisation that preserves the join keysSensitive relations (who bought what) survive the anonymisation
Anonymisation in CI that doesn't anonymise in dev localEngineers have raw prod data on their laptops
Anonymisation as a one-time operationProduction data changes; anonymisation must run continuously

Pattern-selection guide

NeedPatternWhen to mix
Few-fields, default-most strategyTest Data BuilderUse Factory underneath the Builder for DB persistence
Many variants of one entityFactory with traitsCombine with Builder for the test-API surface
Small stable set of canonical objectsObject MotherGenerally legacy; consider migrating to Builder + Factory
Per-test independenceFresh FixtureAlways the default; reach for Shared only when measured slow
Read-only shared stateShared FixtureDocument immutability; one mutation kills the contract
Large structured outputSnapshot / golden-fileSee golden-file-conventions for operational details
Production-shaped privacy-safe dataAnonymisationAlways for production-sourced data; pair with synthetic-pii-generator

Cross-cutting anti-patterns

Anti-patternWhy it fails
Mixing all six patterns in one codebaseEngineers can't tell what to write; vocabulary fragments
Test data inline-literaled in tests ({ name: "Alice", id: 1 }) at scaleRefactors break 200 tests when one schema field changes
Test data setup that takes >5s per testSuite time becomes infeasible; teams skip tests
Data construction and persistence collapsed into one method (createUser() always writes to DB)Cannot test the construction logic without the DB
Implicit Setup (relies on global state from a previous test)Tests become order-dependent; flake follows
Test data with PII / production keysCompliance + security breach surface

Hand-off targets

  • Configure a specific per-language tool → factory-bot-data (Ruby), faker-data (JS), mimesis-data (Python), bogus-data (.NET).
  • Build an E2E seed dataset → seed-data-curator.
  • Generate PII (anonymised) test data → synthetic-pii-generator.
  • Snapshot / golden-file operational details → golden-file-conventions.
  • Generate negative / boundary / malicious test data → negative-test-generator, boundary-value-generator, malicious-payload-bank.
  • Cross-test isolation / fixture scope rules → test-isolation-patterns (sister catalog, in the qa-test-review plugin).
  • Object-model architecture patterns → object-model-patterns (sister catalog).

References

  • Nat Pryce - Test Data Builders (the canonical reference for the Builder pattern as applied to test data): http://www.natpryce.com/articles/000714.html
  • Nat Pryce and Steve Freeman - Growing Object-Oriented Software, Guided by Tests (2009), chapter 22: https://www.growing-object-oriented-software.com/
  • Martin Fowler - Object Mother (canonical reference for the Object Mother pattern): https://martinfowler.com/bliki/ObjectMother.html
  • Martin Fowler - Eradicating Non-Determinism in Tests (Fresh Fixture vs Shared Fixture trade-off, the load-bearing quote on test isolation): https://martinfowler.com/articles/nonDeterminism.html
  • Gerard Meszaros - xUnit Test Patterns: Refactoring Test Code (2007) (the seminal reference for fixture patterns; cite by book ISBN 978-0131495050).
  • thoughtbot - factory_bot (Ruby; the canonical Factory implementation): https://github.com/thoughtbot/factory_bot
  • FactoryBoy team - factory_boy (Python equivalent): https://github.com/FactoryBoy/factory_boy
  • thoughtbot - fishery (TypeScript Factory): https://github.com/thoughtbot/fishery
  • bchavez - Bogus (.NET; Faker-style with factory affordances): https://github.com/bchavez/Bogus
  • Jest - Snapshot Testing (the cross-language reference for the snapshot pattern): https://jestjs.io/docs/snapshot-testing
  • Michael Feathers - Working Effectively with Legacy Code (the characterisation-tests progenitor of golden-file testing): ISBN 978-0131177055.
  • Wikipedia - Test fixture (Meszaros's four-phase test pattern): https://en.wikipedia.org/wiki/Test_fixture
  • 2025 Verizon DBIR - cited for the cross-environment data leakage risk in production-data testing: https://www.verizon.com/business/resources/reports/dbir/
  • ISTQB glossary - test data: https://glossary.istqb.org/en_US/term/test-data
  • ISTQB glossary - fixture: https://glossary.istqb.org/en_US/term/test-fixture
  • ISO/IEC 25024 - data quality model (cited for anonymisation requirements).
  • Anonymisation / synthetic-data tooling: Tonic.ai (https://www.tonic.ai/), Gretel.ai (https://gretel.ai/), K2view (https://www.k2view.com/).
  • factory-bot-data, faker-data, mimesis-data, bogus-data, synthetic-pii-generator, golden-file-conventions, seed-data-curator - the per-tool and operational siblings.
  • object-model-patterns, test-isolation-patterns, test-step-design-patterns - sister architecture-tier pattern catalogs.

Related skills

bogus-data

Authors .NET test fixtures using the Bogus library - fluent typed `Faker` builders with `.RuleFor` per property, generation via `Generate()` / `GenerateBetween(min, max)` / `GenerateLazy()`, and `UseSeed()` for reproducibility. Provides the Bogus equivalent of Python's Faker / Ruby's FactoryBot. Use when the project is C# / F# / VB.NET and the team needs typed fixture creation.

boundary-value-generator

Generates boundary-value test cases from typed input specifications - for each input field, produces the canonical 6-point set (one below, at, and above the lower bound; one below, at, and above the upper bound) plus equivalence-class representatives. Emits cases as parameterized test inputs (pytest @parametrize / Jest test.each / xUnit InlineData / etc.). Use when a function or endpoint has numeric / string-length / collection-size constraints and the team needs systematic edge-case coverage.

e2e-test-narrative-builder

Assembles a multi-step end-to-end user-journey test from a list of high-level user intents - translates each intent ("user signs up", "user adds product to cart", "user completes checkout with promo code") into the corresponding test-runner step (Playwright / Cypress / Selenium / Karate), wires shared state across steps via test fixtures, and emits the resulting test as a single Scenario in the project's E2E framework. Use when scaffolding an E2E test that exercises a complete user flow rather than a single page.

factory-bot-data

Authors Ruby FactoryBot factories with traits, associations, sequences, and the three build strategies (build / create / build_stubbed); integrates with RSpec / Minitest test suites; pairs with Faker for randomized field values. Use when the project is Ruby / Rails and needs structured fixture creation with referential integrity.

faker-data

Authors test-data factories using Faker: the Python `faker` library, the `@faker-js/faker` JS port, and the `faker-ruby` gem. Owns the library mechanics end to end: install per language, the provider catalogue (person / internet / location / date / finance / lorem), locale selection and multi-locale mode, and seed-based determinism for reproducible runs. Scope is generating fresh values for tests that start from nothing, not replacing values inside an existing dataset that already holds real records, which raises referential-integrity and re-identification concerns this skill does not address. Prefer this skill when the codebase already uses the Faker family or when cross-language consistency across Python, JS, and Ruby matters; use mimesis-data only when deeper Python locale coverage is the primary requirement. Use when authoring fixtures or factories that need realistic-looking field values.

golden-file-conventions

Reference catalog for snapshot / golden file management - naming conventions, directory layout, when to add / update / remove a baseline, sanitization (timestamps, IDs, PII), per-OS / per-runtime variant strategy, and review workflow for snapshot diffs in PRs. Use when designing a snapshot-testing convention or auditing an existing one for drift.

malicious-payload-bank

Reference catalog of curated adversarial input payloads keyed by attack class - SQL injection, XSS, SSRF, path traversal, command injection, XXE, prototype pollution, regex DoS, Unicode confusables, header injection - plus per-context guidance for which payloads apply (URL parameter / form input / JSON body / file upload). Use when authoring negative-test cases for input validation, fuzz targets, or a security-focused test suite that needs to exercise the OWASP Top 10 attack surface.

mimesis-data

Authors Python test fixtures using mimesis - a fast, type-hinted, locale-aware test-data generator with 46 locales - covering Person / Address / Internet / Datetime providers and the Schema/Field pattern for typed-dict generation. Pairs with factory_boy when referential integrity is needed. Use when the project is Python and the team values speed, type hints, or strong locale coverage over Faker's larger ecosystem.

mountebank-imposters

Authors Mountebank imposters (multi-protocol mock servers - HTTP, HTTPS, TCP, SMTP, LDAP, gRPC, WebSockets, GraphQL, and more) by POSTing JSON definitions to the Mountebank control API on port 2525, configures stubs with predicates and responses, and uses record-playback proxy mode to capture upstream traffic. Use when the project needs a multi-protocol mock server beyond HTTP-only tools like WireMock or MSW.

msw-handlers

Authors Mock Service Worker (MSW) request handlers for both browser and Node.js test environments using the `http.get` / `http.post` / `HttpResponse.json` API, wires them via `setupWorker` (browser) or `setupServer` (Node), and manages the test lifecycle (`server.listen` / `resetHandlers` / `close`). Use when the project uses JavaScript / TypeScript and needs to mock fetch / XHR at the network layer for both Vitest / Jest unit tests and Cypress / Playwright integration tests.

negative-test-generator

Generates negative / error-path test cases that mirror happy-path tests - for each happy-path test, produces companions exercising input validation rejection, missing required fields, type mismatches, authorization failures, rate-limit errors, and adversarial payloads from the malicious-payload-bank. Emits cases as parameterized tests in the project's runner format. Use when a feature has happy-path coverage but the rejection / error / unauthorized paths are untested.

pairwise-test-case-generator

Generates parameterized test inputs combining boundary-value, equivalence-class, and pairwise-combinatorial cases from a typed multi-input specification - produces the cross-product of cases up to a configurable strength (1-wise / 2-wise / N-wise) using all-pairs reduction so the test surface stays tractable. Emits cases in the project's test-runner-native parametrize format. Use when a function or endpoint takes 3+ inputs whose interactions matter and full Cartesian product would explode.

seed-data-curator

Builds a reproducible E2E seed dataset for the project's test environments - picks a representative user / org / data-product cross-section, generates the rows via the project's chosen factory library (FactoryBot / mimesis / Bogus / Faker + factory_boy), persists the dataset as a checked-in fixture (SQL dump / JSON / per-engine seed file), and wires it into the test bootstrap. Use when starting E2E coverage on a project that has no seed strategy, or when an existing seed has drifted.

synthetic-data-tool-selector

Chooses between the four mainstream synthetic test-data generators - Faker (JavaScript), FactoryBot (Ruby), mimesis (Python), Bogus (.NET) - picks the right tool by language and use case (raw value generation vs. typed factory orchestration), shows side-by-side equivalents for the same fixture across all four, and emits the language-appropriate code. Use when starting test-data work on a project and the team wants the "which tool should I use" decision documented.

synthetic-pii-generator

Generates realistic-but-fake personally identifiable information (PII) - emails, phone numbers, SSNs / national IDs, addresses, names, credit-card numbers (test BIN ranges), date-of-birth - for non-production environments. Wraps Faker / mimesis with PII-aware constraints so generated values match real format expectations (Luhn-valid card numbers, region-valid phone formats, ITIN/SSN format) without ever generating real-person data. Use when seeding test environments, building demo data, or replacing real PII in copied datasets.

wiremock-stubs

Authors WireMock stub mappings for HTTP service mocking - `stubFor` with verb/path/header matchers + `willReturn` response shaping, lifecycle via `WireMockServer` (start / stop) or JUnit `WireMockExtension`, request verification via `verify()`, and dynamic-port allocation for parallel tests. Use when the project is JVM-based and tests need to mock HTTP dependencies (third-party APIs, internal microservices) at the network layer.