QA How-To
Building a synthetic test data generator (2026)
Learn building a synthetic test data generator with schemas, constraint solving, safe edge cases, privacy controls, deterministic seeds, and CI verification.
25 min read | 3,092 words
TL;DR
Building a synthetic test data generator starts with schemas, relationships, business invariants, and scenario intent. Generate deterministically, validate every batch, isolate intentionally invalid records, scan for privacy leakage, and use AI only to propose cases or mappings that deterministic controls can verify.
Key Takeaways
- Generate from an explicit domain model and invariants, not from column types alone.
- Separate schema-valid, business-valid, boundary, and intentionally invalid scenarios.
- Create parent records before children and verify referential and aggregate constraints.
- Use deterministic seeds and immutable scenario manifests for reproducible failures.
- Never treat masked production data as automatically synthetic or privacy safe.
- Let AI propose scenarios or mappings, but validate all generated records in code.
- Measure validity, coverage, privacy leakage, uniqueness, and defect-finding value.
Building a synthetic test data generator means creating artificial but purposeful records that satisfy technical schemas, preserve domain relationships, exercise business states, and contain no copied real identity. A reliable generator produces reproducible scenario manifests, validates every batch, and clearly separates valid fixtures from deliberate negative data.
The hard part is not inventing names or numbers. It is modeling rules across customers, orders, payments, entitlements, dates, locales, and lifecycle states without creating impossible combinations. This guide shows a layered architecture, a runnable standard-library generator, privacy and security controls, AI-assisted scenario design, CI integration, and evaluation methods for working QA and SDET teams.
TL;DR
| Data goal | Generator behavior | Required check |
|---|---|---|
| Happy path | Satisfy schema and business invariants | Full validation passes |
| Boundary | Hit defined minimum, maximum, rollover, or threshold | Boundary tag and oracle are present |
| Rare valid state | Satisfy a low-frequency but permitted rule combination | Scenario preconditions are proven |
| Negative test | Break exactly one named rule where practical | Expected rejection is recorded |
| Load volume | Preserve distributions and relationships at scale | Counts, skew, and performance budget |
| Privacy | Create artificial identities and content | Leakage and memorization scans pass |
| Reproduction | Recreate the exact batch and scenario | Seed, generator version, and manifest match |
Do not call random values realistic merely because they look familiar. Test data is useful when its constraints and expected outcomes are known.
1. What Building a Synthetic Test Data Generator Involves
A synthetic test data generator transforms a domain model and scenario request into records, setup actions, and an oracle. It should know the difference between structural validity, business validity, and scenario intent. A date can match an ISO format while violating a rule that activation must precede cancellation. An order can reference a valid customer while using a currency the merchant does not support.
The generator has four responsibilities. First, construct values within schema constraints. Second, maintain relationships and lifecycle invariants. Third, target meaningful partitions and boundaries rather than produce undirected randomness. Fourth, emit provenance that makes the result reproducible and disposable.
Synthetic does not mean anonymous production data. Truly synthetic records are generated without copying a real person's row. Masked or transformed production datasets can retain sensitive combinations, rare attributes, free text, and linkage risk. Treat those as a different data product with a separate privacy review.
Define expected use before architecture. Unit tests need small in-memory objects. API tests may need setup endpoints. end-to-end tests need coherent cross-service state. Performance tests need controlled volume, skew, and event timing. Model evaluation may need adversarial text. One generator can share a domain core, but adapters and policies should remain tailored to each environment.
2. Model Schemas, Relationships, and Business Invariants
Start with a canonical data model derived from approved schemas and domain rules. Capture type, nullability, format, length, enumerations, uniqueness, default behavior, and conditional requirements. Add relationships such as customer-to-order, order-to-line, subscription-to-entitlement, and account-to-region. Record cascade and deletion behavior.
Business invariants need names and executable checks. Examples include order_total_equals_line_sum, refund_not_above_captured_amount, end_after_start, active_subscription_has_entitlement, and currency_supported_by_merchant. A human-readable description helps review, while code provides enforcement.
Represent conditional rules explicitly. A company account may require tax details while an individual account forbids them. A shipped order requires a shipment record, but a digital order does not. Randomly generating each column independently will create contradictions. Generate a high-level state first, then derive dependent fields.
Assign authority and version to the model. Database DDL explains storage, an API schema explains accepted payloads, and product requirements explain behavior. They can conflict. Surface the disagreement instead of silently choosing one. A generator release should state which contract versions it implements and which invariants remain unresolved. This creates useful feedback for writing a test plan and requirement reviews.
3. Design a Scenario Language Around Intent
A scenario request should describe purpose rather than dozens of raw field overrides. For example, checkout.success.domestic_card, refund.partial.at_limit, subscription.renewal.payment_declined, or profile.update.invalid_unicode_length. The generator maps intent to entities, states, constraints, and expected results.
Each scenario definition needs:
- Stable scenario ID and semantic version.
- Preconditions and required capabilities.
- Parameters with allowed ranges.
- Entities and lifecycle events to generate.
- Invariants that must hold.
- A negative rule to break, when applicable.
- Expected API, UI, database, or event outcome.
- Cleanup strategy and time-to-live.
- Privacy classification and permitted environments.
Keep valid and invalid generation separate. A broad invalid=true flag produces unclear tests. Specify violation=refund_above_capture or violation=missing_required_country, then satisfy all other relevant rules as far as practical. This isolates the reason for rejection and strengthens the oracle.
Support controlled combinations through pairwise or coverage models when many dimensions interact, but retain hand-authored critical scenarios. Generation algorithms optimize a declared model, so missing risks remain missing. Review scenario catalogs with product, development, security, and support evidence, including production incidents without copying their sensitive data.
4. Generate in Dependency Order and Validate Twice
Build a dependency graph of entity types and generate parents before children. A tenant precedes users, a catalog item precedes an order line, an order precedes payment and shipment. Cycles such as manager relationships require a two-phase approach: create records with deferred links, then resolve references.
Validation happens before and after persistence. Pre-write validation catches schema, uniqueness, referential, and business errors in the generated batch. Post-write validation confirms that APIs, defaults, triggers, event processors, and eventual consistency produced the expected state. A successful HTTP response is not proof that every related record is correct.
Use transactions where the target supports them, but do not assume a distributed setup is atomic. Give every batch a unique namespace and scenario run ID. If setup partially fails, record created resources and run compensating cleanup. Cleanup must filter by namespace, never by broad timestamps or guessed prefixes.
For eventually consistent systems, poll a named condition with a deadline and bounded interval. Record intermediate state, then fail with diagnostic evidence. Avoid fixed sleeps that either slow tests or race the system. A scenario manifest should distinguish requested records, accepted writes, observed final state, and cleanup outcome.
5. Implement a Runnable Deterministic Generator
This standard-library Python example creates artificial customers and orders, preserves references, calculates totals from line items, and validates invariants. It uses the reserved example.test domain, so generated addresses cannot be mistaken for real mailboxes. Save it as generate_data.py and run python generate_data.py 42 5.
from __future__ import annotations
import json
import random
import sys
from dataclasses import asdict, dataclass
from decimal import Decimal
@dataclass(frozen=True)
class Customer:
customer_id: str
email: str
country: str
@dataclass(frozen=True)
class Order:
order_id: str
customer_id: str
currency: str
line_cents: tuple[int, ...]
total_cents: int
def generate(seed: int, count: int) -> tuple[list[Customer], list[Order]]:
rng = random.Random(seed)
customers: list[Customer] = []
orders: list[Order] = []
for number in range(1, count + 1):
customer_id = f"syn-{seed}-customer-{number}"
country = rng.choice(("US", "CA", "GB"))
customer = Customer(customer_id, f"user-{seed}-{number}@example.test", country)
prices = tuple(rng.randint(100, 20_000) for _ in range(rng.randint(1, 4)))
order = Order(
order_id=f"syn-{seed}-order-{number}",
customer_id=customer_id,
currency={"US": "USD", "CA": "CAD", "GB": "GBP"}[country],
line_cents=prices,
total_cents=sum(prices),
)
customers.append(customer)
orders.append(order)
return customers, orders
def validate(customers: list[Customer], orders: list[Order]) -> None:
customer_ids = {customer.customer_id for customer in customers}
assert len(customer_ids) == len(customers), "duplicate customer ID"
for order in orders:
assert order.customer_id in customer_ids, "orphan order"
assert order.total_cents == sum(order.line_cents), "invalid order total"
assert all(Decimal(value) > 0 for value in order.line_cents), "nonpositive line"
if __name__ == "__main__":
seed_value, record_count = int(sys.argv[1]), int(sys.argv[2])
generated_customers, generated_orders = generate(seed_value, record_count)
validate(generated_customers, generated_orders)
print(json.dumps({
"seed": seed_value,
"customers": [asdict(item) for item in generated_customers],
"orders": [asdict(item) for item in generated_orders],
}, indent=2))
The seed reproduces choices, but only when generator code and scenario version are also fixed. Store all three. For security-sensitive values, use the secrets module rather than a seeded pseudo-random generator, and accept that exact value reproduction may be inappropriate.
6. Target Boundaries, Partitions, and Stateful Sequences
Random data tends to cluster in ordinary states and miss precise boundaries. Build explicit generators for empty, minimum, just below, exactly at, just above, and maximum values. Include Unicode normalization, right-to-left text, leap dates, daylight-saving transitions, currency precision, long identifiers, duplicate requests, and absent optional fields when the domain supports them.
Partition by behavior, not only data type. An account can be new, verified, suspended, closed, pending deletion, or migrated. An order can be authorized, captured, partially refunded, fully refunded, disputed, or expired. Generate valid state transitions and attempt invalid transitions with a named expected rejection.
Use model-based generation for sequences when rules depend on history. The model defines states, commands, guards, and expected transitions. The generator walks permitted paths, records commands, and shrinks a failing sequence if the framework supports it. Even without a property-testing library, saving the exact command sequence makes a failure replayable.
Pairwise combination can reduce a large configuration matrix, but not every factor has equal consequence. Force critical combinations such as region plus currency plus tax regime, authentication mode plus role, or browser plus accessibility setting. Document excluded combinations and why they are impossible. A decision table testing guide can turn complex conditional rules into a reviewable scenario model.
7. Preserve Realism Without Copying Production People
Realism means preserving behavior-relevant structure, not copying names and histories. For performance tests, useful properties may include order-size distribution, relationship fan-out, event timing, text length, geographic allocation, and hot-key skew. Estimate or define these properties at an aggregate level under privacy review, then generate new values.
Avoid fake data that accidentally becomes operational. Use reserved domains, clearly synthetic phone ranges where local rules permit, impossible external delivery routes, test payment instruments supplied by the payment provider, and visible name prefixes. Block synthetic accounts from marketing, billing, analytics, fraud reports, and customer notifications through environment and account flags enforced server-side.
Free text is high risk. It can contain real names, addresses, health information, credentials, or offensive content. Prefer controlled templates and curated vocabularies. If an AI model generates descriptions, constrain topic, length, language, and prohibited content, then scan and review representative samples. Do not prompt a model with raw production notes.
Statistical similarity can create re-identification risk when rare attribute combinations are preserved. Establish minimum group sizes or other approved privacy controls, remove unnecessary dimensions, and test membership or nearest-neighbor risk where applicable. QA should work with privacy and security specialists rather than claim that an absence of exact rows proves safety.
8. Use AI for Scenario Discovery, Not Constraint Enforcement
AI can read approved requirements, schemas, incident summaries, and domain rules to propose missing scenarios, field mappings, and adversarial combinations. It is useful for brainstorming interaction risks and converting prose into draft scenario definitions. Every proposal should cite its source, list assumptions, and distinguish required behavior from exploratory testing.
Deterministic code must validate schema, formats, enumerations, references, arithmetic, lifecycle, authorization, environment, and cleanup. Generated JSON that looks valid can still violate a cross-entity invariant. Reject unknown fields and scenario IDs. A model must not invent a database table, API endpoint, test card, or environment credential.
Constrain AI output to a schema such as scenario_id, purpose, source_ids, entities, constraints, boundary, expected_result, and assumptions. Route the draft through human review before it joins the versioned catalog. If evidence conflicts, preserve the conflict and block automatic generation for that scenario.
Treat requirements and incident text as untrusted prompt content. The AI has no direct database tool or secret access. A separate orchestrator selects approved templates, validates outputs, invokes deterministic generators, and applies environment policy. If AI is unavailable, reviewed scenario definitions still run. This keeps a valuable test-data capability from depending entirely on generative service availability.
9. Protect Privacy, Security, and Environment Boundaries
Classify every field and scenario. Prohibit secrets, real credentials, production tokens, customer identifiers, unreviewed biometrics, and sensitive free text. Use separate synthetic namespaces and accounts. Enforce destination allowlists so a generator configured for test cannot write to production, even if a caller changes a URL parameter.
Service credentials should have create, read, and cleanup permissions only for the synthetic namespace. Avoid broad delete permission. Validate TLS, use secret managers, rotate credentials, and never print them in manifests or logs. Scan generated output and logs before retention or model submission.
Test privacy controls with seeded canary values that resemble prohibited patterns but are safe. Verify detection, quarantine, alerting, and deletion. Also test false positives so the pipeline does not become unusable. Periodically compare generated data against restricted reference fingerprints through an approved privacy process, not by exposing production rows to the test team.
Set time-to-live and cleanup ownership. A scheduled janitor can remove expired records by signed namespace and manifest, while individual test teardown handles normal cleanup. Monitor orphan rate, cleanup errors, storage growth, and accidental downstream delivery. If cleanup fails, block further large batches before they exhaust shared environments.
10. Verify Quality With Data Contracts and Metamorphic Tests
Every batch should pass structural, relational, business, privacy, distribution, and scenario-oracle checks. Structural validation covers types and formats. Relational validation finds orphans and duplicates. Business validation checks named invariants. Privacy validation scans prohibited patterns and uniqueness concerns. Distribution checks compare declared targets, not undocumented production copies.
Metamorphic testing is powerful when exact outputs vary. With the same seed and versions, output should be identical. Increasing count should preserve the first records if that is part of the contract. Changing locale should alter locale-dependent values but not unrelated identity relationships. Adding an order line should increase total by that line's amount.
Track useful metrics:
| Metric | Meaning | Failure response |
|---|---|---|
| Valid-batch rate | Intended-valid batches passing all invariants | Block publication and diagnose rule |
| Negative isolation | Invalid scenarios breaking only the named rule | Repair scenario builder |
| Referential integrity | Children with valid parents | Reject entire batch |
| Boundary coverage | Declared edges exercised | Add targeted scenarios |
| Reproducibility | Same manifest yielding same permitted output | Version missing dependency |
| Privacy leakage | Prohibited or suspicious matches | Quarantine and investigate |
| Cleanup success | Created resources removed on schedule | Pause volume and remediate |
A high valid-batch rate is not enough if generated data never reveals defects. Track which scenarios expose unique failures and which are redundant. Retire low-value random volume while retaining coverage required for scale or distributions.
11. Integrate Generation Into Tests and CI
Offer generators as libraries for unit tests, a command-line tool for CI, and a controlled service only when multiple languages or environments require it. All interfaces should accept a scenario ID, scenario version, seed, namespace, count, and permitted parameters, then return a manifest and cleanup handle. Avoid a free-form prompt as the primary API.
Generate as close to test execution as practical. Long-lived shared fixtures accumulate drift and cross-test interference. For expensive setup, use immutable snapshots built by a validated pipeline, label them with contract versions, and refresh them intentionally. Never allow parallel jobs to share mutable synthetic identities without coordination.
CI should publish the scenario manifest, not necessarily all sensitive payloads. Record generator build, schema versions, seed, parameters, created IDs, validation results, setup timing, and cleanup result. On failure, preserve enough authorized evidence to replay. Redact values that function as credentials even in test.
Separate a generator failure from a product failure. If setup cannot create a valid prerequisite, mark the test blocked or infrastructure-failed according to policy, not product-failed. If the product correctly rejects an intentionally invalid record, the negative scenario passes. Clear oracles and phases prevent synthetic data problems from corrupting defect metrics.
12. Operating Building a Synthetic Test Data Generator
Assign owners for domain rules, schema adapters, scenario catalogs, privacy controls, environment connectors, and cleanup. A central platform can own manifests and security while domain teams own invariants. Changes to public generator behavior need compatibility tests because hundreds of test suites may depend on deterministic output.
Version everything that affects generation: code, scenario, domain schema, reference vocabulary, locale rules, time provider, and dependency data. Inject time rather than reading the wall clock deep inside generators. Stable seeds do not reproduce output when an unrecorded current date changes age, expiry, or billing state.
Monitor schema rejection, invariant failure, generation latency, collision, privacy quarantine, stale scenarios, unused scenarios, environment write errors, and cleanup. Run canary scenarios after target service changes. If an API contract changes, block incompatible generator versions with a clear migration path.
Improve the system from observed gaps. Repeated field overrides indicate a missing scenario parameter. Frequent post-write failures may reveal an undocumented server rule. Slow cleanup may indicate inadequate namespace design. AI-generated suggestions can expand review input, but production reliability comes from executable invariants and controlled adapters. The generator is successful when teams can create meaningful state quickly, reproduce defects, and delete that state safely.
Interview Questions and Answers
Q: What makes synthetic test data high quality?
It satisfies declared schemas and business invariants, covers meaningful partitions and boundaries, preserves relationships, contains no copied identity, and has a known oracle. It is also reproducible and safely disposable. Realistic appearance alone is insufficient.
Q: How do you maintain referential integrity?
Model entity dependencies, generate parents before children, and resolve cycles in a second phase. Validate references before persistence and verify target state afterward. Use one namespace and manifest to support safe cleanup.
Q: Why is a random seed not enough for reproduction?
Output also depends on generator code, scenario version, schemas, vocabularies, locale, and time. Record all of them and inject time. Security-sensitive randomness may intentionally be non-reproducible.
Q: Can masked production data be called synthetic?
Not automatically. Transformation can retain sensitive relationships, rare combinations, and free text. Treat masked data as a separate privacy-reviewed product and prefer generation from approved aggregate properties where possible.
Q: Where should AI be used?
Use it to propose scenarios, extract candidate rules, and map approved requirements to draft test intent. Deterministic validators enforce every schema, relationship, arithmetic, lifecycle, security, and environment rule before data is used.
Q: How do you generate negative data?
Name one rule to violate, construct a record that satisfies the remaining relevant rules, and attach the expected rejection. This isolates the oracle and avoids meaningless records that fail for many reasons.
Q: How do you prevent synthetic emails from reaching users?
Use reserved domains and synthetic account flags, then block outbound systems server-side in test environments. Also configure delivery sinks, destination allowlists, and monitoring. Do not rely only on recognizable fake names.
Common Mistakes
- Generating independent random columns without cross-field or lifecycle constraints.
- Calling masked production records synthetic and assuming privacy is solved.
- Mixing intentionally invalid records into a valid-data batch.
- Using random data without a recorded seed, scenario, time, and generator version.
- Creating realistic email addresses that can route to real people.
- Letting AI-generated JSON bypass deterministic validation.
- Using broad cleanup queries that can delete another test's records.
- Measuring schema validity while ignoring boundary and defect-finding coverage.
- Allowing old fixtures to drift from current service contracts.
Conclusion
Building a synthetic test data generator is domain modeling turned into executable QA infrastructure. Start from schemas and invariants, generate dependent state in order, target named boundaries and lifecycles, validate before and after persistence, and preserve a reproducible manifest. Keep artificial identity, privacy review, and cleanup central to the design.
Begin with one valuable domain scenario and implement its valid, boundary, and single-rule negative forms. Add deterministic verification and environment-safe cleanup before increasing volume or AI assistance. A mature generator gives tests exactly the state they need without borrowing risk from production data.
Interview Questions and Answers
How would you architect a synthetic test data generator?
I would separate a versioned domain model, scenario catalog, deterministic generation core, validators, environment adapters, manifest store, and cleanup service. Parent entities are generated first and every batch is checked before and after persistence. AI proposals never bypass executable constraints.
How do you ensure generated records are realistic and safe?
I preserve only behavior-relevant aggregate properties such as cardinality, skew, timing, and valid relationships. Identities and free text are newly created from controlled sources. Reserved destinations, privacy scans, downstream blocks, and access controls prevent accidental contact or leakage.
How do you handle circular entity relationships?
I generate records in two phases, first creating stable identities with deferred links, then resolving the cycle. Both phases are validated and represented in the manifest. If the target cannot update links transactionally, compensating cleanup handles partial state.
How would you test the generator itself?
I use schema and invariant unit tests, referential checks, metamorphic properties, known-seed snapshots, negative-rule isolation, privacy canaries, adapter contract tests, and post-write integration checks. Distribution and performance tests cover large batches. Cleanup and partial failure are first-class scenarios.
What is the role of deterministic seeds?
Seeds make pseudo-random choices replayable when all other dependencies are versioned. I record code, scenario, schema, vocabulary, and time alongside the seed. I do not use seeded randomness for values that must be cryptographically unpredictable.
How would you use AI in test data generation?
I would use AI to extract draft constraints from approved documents and propose boundary or interaction scenarios with citations and assumptions. A reviewer approves the scenario definition. Deterministic code then generates and validates records, with no model credentials to target environments.
How do you distinguish a product defect from setup failure?
The manifest separates generation, validation, persistence, observed readiness, test execution, and cleanup phases. A failure before valid preconditions exist is setup or infrastructure according to policy. A product defect requires the expected preconditions and a violated product oracle.
Frequently Asked Questions
What is a synthetic test data generator?
It is software that creates artificial records and state for testing from explicit schemas, relationships, business rules, and scenarios. Good generators also emit expected outcomes, provenance, reproducibility metadata, and cleanup handles.
Is synthetic test data always privacy safe?
No. Generated data can resemble real people, memorize source text, preserve rare combinations, or reach external systems. Use approved inputs, reserved identifiers, leakage scans, access control, environment isolation, and privacy review.
How do you make generated test data reproducible?
Record the seed, generator build, scenario and schema versions, reference vocabularies, parameters, and injected time. Store them in an immutable manifest and retain the exact command sequence for stateful scenarios.
How should invalid test data be generated?
Choose one named constraint to violate and satisfy other relevant rules wherever practical. Attach the expected validation error or rejection behavior so the test has a precise oracle.
Can AI generate test data directly?
AI can propose scenarios and draft values, but generated output must pass deterministic schema, business, security, privacy, and environment validation. The model should not receive direct production database access.
How do you clean up synthetic test data?
Namespace every batch, retain exact created IDs in a manifest, use least-privilege cleanup adapters, and set a time-to-live janitor. Monitor failures and avoid broad timestamp or prefix deletion rules.
What metrics show test data generator quality?
Track intended-valid batch rate, negative-rule isolation, referential integrity, boundary and state coverage, reproducibility, privacy leakage, uniqueness, setup performance, cleanup success, and unique defects revealed.