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.
75
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
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.
Do not use this skill to:
negative-test-generator, boundary-value-generator, pairwise-test-case-generator.seed-data-curator.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();| Anti-pattern | Why 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 one | Test 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 fields | Overhead exceeds benefit; use a struct literal |
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):
factory_bot - the origin.factory_boy - direct port.fishery, @faker-js/faker (lower-level).Bogus - Faker-style with factory affordances.data-faker + handwritten factory or test-data-builder-style libraries.| Anti-pattern | Why 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 domain | Becomes a god-object; every test pulls a fully-populated graph |
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.
| Anti-pattern | Why it fails |
|---|---|
| Object Mother that grows to 50+ methods | Becomes a god-class; engineers can't find the right factory |
| Methods that return shared mutable instances | Tests 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 |
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:
| Flavour | When |
|---|---|
| Fresh Fixture | Each test creates its own state from scratch. Most isolated; slowest. |
| Shared Fixture | Multiple tests share one initialised state. Fastest; brittle to test ordering. |
| Persistent Fresh Fixture | Fresh 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): "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."
| Anti-pattern | Why it fails |
|---|---|
| Shared Fixture that some tests quietly mutate | Cross-test coupling; failures depend on test order |
| Fresh Fixture for a 30-minute E2E seed | Test suite time becomes infeasible; team starts skipping tests |
| Multiple fixture flavours in the same suite without explicit convention | Engineers can't tell what to write; bugs creep in |
| Fixture inheritance hierarchies >2 levels deep | Depth-3+ chains break unpredictably |
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.
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).
| Flavour | Definition | When |
|---|---|---|
| Masking | Replace sensitive fields with deterministic placeholders (X*** for surnames; static fake date) | Production-shape preserved; field-level reversible if needed |
| Synthesis | Generate fake data that statistically resembles production (length distributions, locale mix) | No mapping back to production; safest |
| Tokenisation | Replace sensitive values with tokens that map back via a secured lookup | When the test environment needs to round-trip data to production (rare in QA) |
| Anti-pattern | Why it fails |
|---|---|
| Copying production DB to staging "for realism" | Cross-environment data leakage; GDPR / CCPA / HIPAA breach surface |
| Anonymisation that preserves the join keys | Sensitive relations (who bought what) survive the anonymisation |
| Anonymisation in CI that doesn't anonymise in dev local | Engineers have raw prod data on their laptops |
| Anonymisation as a one-time operation | Production data changes; anonymisation must run continuously |
| Need | Pattern | When to mix |
|---|---|---|
| Few-fields, default-most strategy | Test Data Builder | Use Factory underneath the Builder for DB persistence |
| Many variants of one entity | Factory with traits | Combine with Builder for the test-API surface |
| Small stable set of canonical objects | Object Mother | Generally legacy; consider migrating to Builder + Factory |
| Per-test independence | Fresh Fixture | Always the default; reach for Shared only when measured slow |
| Read-only shared state | Shared Fixture | Document immutability; one mutation kills the contract |
| Large structured output | Snapshot / golden-file | See golden-file-conventions for operational details |
| Production-shaped privacy-safe data | Anonymisation | Always for production-sourced data; pair with synthetic-pii-generator |
| Anti-pattern | Why it fails |
|---|---|
| Mixing all six patterns in one codebase | Engineers can't tell what to write; vocabulary fragments |
Test data inline-literaled in tests ({ name: "Alice", id: 1 }) at scale | Refactors break 200 tests when one schema field changes |
| Test data setup that takes >5s per test | Suite 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 keys | Compliance + security breach surface |
factory-bot-data (Ruby), faker-data (JS), mimesis-data (Python), bogus-data (.NET).seed-data-curator.synthetic-pii-generator.golden-file-conventions.negative-test-generator, boundary-value-generator, malicious-payload-bank.test-isolation-patterns (sister catalog, in the qa-test-review plugin).object-model-patterns (sister catalog).factory_bot (Ruby; the canonical Factory implementation): https://github.com/thoughtbot/factory_botfactory_boy (Python equivalent): https://github.com/FactoryBoy/factory_boyfishery (TypeScript Factory): https://github.com/thoughtbot/fisheryBogus (.NET; Faker-style with factory affordances): https://github.com/bchavez/Bogusfactory-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.