You create realistic test data by transforming real production data so it keeps its structure, relationships, and statistical shape while shedding anything sensitive — then filling the gaps with synthesized values where production is thin or missing. Three techniques do most of the work: masking and de-identification to make real data safe, subsetting to produce smaller slices that stay referentially intact, and synthesis to generate values that match your data's patterns. The aim is data that behaves like production in testing without carrying production's risk.
What makes test data realistic (and where fakes fall short)
Test data is realistic when it behaves like production across four dimensions at once, and missing any one of them lets defects slip through. The first is structural: the schema, data types, field formats, and valid value ranges have to match, so a column that holds a formatted phone number in production holds one in your test data too. The second is statistical: distributions, cardinality, null rates, and correlations between columns need to hold, because a query planner or a machine learning feature that behaves one way on production's skew will behave differently on a uniform random fill. The third is relational: foreign keys and cross-table relationships must stay intact, or joins return rows that could never occur in the real system. The fourth is behavioral: the data has to carry the real edge cases and anomalies — the expired records, the malformed inputs, the once-in-a-million account state — not just clean happy-path rows.
Naive approaches fail these tests in predictable ways. Random data generators satisfy the schema but flatten the statistics and invent relationships that don't exist, so they exercise the code paths you already expected and miss the ones you didn't. Hand-coded fixtures are precise but tiny, and they drift out of sync with the schema the moment a migration lands. Blanket-nulling or blanking sensitive columns destroys exactly the distributions and edge cases that make data worth testing against. Knowing the types and sources of test data you can draw on is the starting point, and referential integrity — the guarantee that related records stay consistent — is the criterion the naive methods break first. Satisfying all four dimensions safely is what masking, subsetting, and synthesis are each built to do.
| Approach | How it creates realism | Best for | Safety consideration |
|---|---|---|---|
| Masked production data | Transforms real rows so structure, formats, and distributions survive | Data that must mirror production closely | Sensitive values must be fully detected and replaced |
| Subsetting | Takes a referentially intact slice of a larger source | Smaller environments that still behave like production | Relationships must survive the cut |
| Synthesis | Generates values that match the existing schema and distributions | Filling gaps and rare cases production underrepresents | Generated values must not leak or reconstruct real ones |
| Generate from scratch | Builds data from a model with no production dependency | Greenfield apps and volumes production can't supply | Fidelity depends on how well the model is specified |
Start from production: masking and de-identification
The most direct route to realistic data is transforming production so it keeps its shape but sheds its sensitive values — the technique known as masking or de-identification. Done well, masking is format-preserving: a masked email address still looks and validates like an email, a masked Social Security number keeps its nine-digit pattern, and a masked credit card number still passes a checksum. That matters because downstream code validates these formats, and data that fails validation exercises error handling instead of the logic you meant to test. Equally important is consistency: the same input has to map to the same output everywhere it appears, so that a customer ID masked in one table matches the masked version in every table that references it. Without consistent masking, joins and lookups break and the data stops behaving like a real database.
Tonic Structural is a clear example of how this works in practice. Structural scans the schema to automatically detect PII and PHI, so you are not hand-auditing hundreds of columns to find what needs protecting. It then applies a library of more than 50 generators tailored to specific data types — names, addresses, dates, identifiers, and regulatory-specific formats — each of which produces realistic, format-consistent replacements rather than blanked cells. For sensitive types unique to your organization, you can define custom rules, and Structural's built-in AI agent configures and applies generators from plain-language prompts instead of column-by-column manual mapping. The result is data masking for lower environments that removes the sensitive values while leaving the structure and statistics a test needs.
The Tonic Advantage: realism survives de-identification. The failure mode of most masking is that protecting the data ruins it — blank a column and you lose its distribution, its formats, and its edge cases. Structural avoids that by pairing automatic PII and PHI detection with type-specific, format-preserving generators and consistent mapping across tables. A masked dataset comes out safe to move into a lower environment while still validating, joining, and skewing the way production does.
Keep it coherent: referential integrity and subsetting
Realism collapses the moment relationships break, so preserving coherence is what separates usable test data from a pile of valid-looking rows. Masking has to be referentially consistent across foreign keys: if an order references a customer, the masked customer ID on the order must match the masked ID on the customer record, or the join returns nothing. The same principle governs how much data you move. Most teams don't need a full production copy — they need a subset that's smaller but still behaves like the whole. Good subsetting starts from a target, such as a percentage of rows or a WHERE-clause condition, then traverses foreign-key dependencies to pull every related row required to keep the result valid. A subset built that way is a fraction of the size yet still behaves like a complete database, which is why referential integrity has to survive both masking and subsetting rather than being an afterthought to either.
Database subsetting in Tonic Structural is built around this problem. Structural's patented subsetter traverses foreign-key dependencies automatically, so a percentage or WHERE-clause target expands into a complete, referentially intact slice without hand-writing the traversal logic, which keeps subsetting practical on schemas with hundreds of related tables. Because each developer can take an isolated dataset rather than sharing one mutable copy, subsetting also eliminates the test collisions that happen when several people write to the same environment at once. The payoff at scale is real: eBay scaled a multi-petabyte data ecosystem down to manageable subsets that fuel its automated testing, shortening development cycles in the process — the kind of outcome that is only possible when the smaller copy stays as coherent as the source it came from.
Synthesize what production can't give you
Some of the data you need for realistic testing doesn't exist in production yet, and that is where synthesis earns its place. A rare edge case might appear once a year, a brand-new feature has no historical rows at all, and a greenfield application has no production database to draw from. Synthesis fills those gaps by generating values that fit your data's model rather than copying rows that were never there. Two grounded paths cover the range, and they solve different problems rather than competing.
The first stays inside the de-identification workflow. Tonic Structural can synthesize values that match your existing schema and distributions, so where a column is sensitive or a case is underrepresented, Structural generates realistic substitutes that preserve the statistical shape the data had. The second path removes the production dependency entirely. Tonic Fabricate generates test data from scratch or from a model of existing data, which makes it the right tool when there is no production database to start from — a greenfield schema, a mock service, or a feature that hasn't shipped. Describe what you need with a schema or a plain-language prompt and Fabricate produces a relationally intact dataset, and it can stand up mock APIs alongside the data so a service still under development has something realistic to call. Fabricate is also how you scale record counts beyond what production holds: Structural subsets and de-identifies a realistic baseline, then Fabricate models that baseline and generates additional rows, giving you the volumes that performance and load testing need without reintroducing sensitive data. Choosing synthetic test data alongside masked production data is not an either-or — Structural leads the workflow on real data, and Fabricate extends it where real data runs out.
The Tonic Advantage: fill the gaps without importing risk. Production is thin exactly where testing needs depth — the rare states, the new features, the volumes you don't have yet. Structural synthesizes realistic values that match your existing distributions, and Fabricate generates from a model with no production dependency and scales counts past what production can safely provide. Together they cover the cases collected data can't, and none of it carries a sensitive value back into a lower environment.
Make it safe, repeatable, and self-service
Realistic data is only useful if it is also safe and reproducible, so the last part of the job is operational. Safety means keeping sensitive data out of lower environments entirely: de-identify consistently, and make sure there is no path to re-identify an individual from what remains. Reproducibility means the data doesn't rot — a set that was refreshed six months ago no longer reflects current schemas or production behavior, so refreshing on a schedule keeps environments honest. And usability means developers get data when they need it instead of filing a ticket and waiting days, which is where self-service provisioning changes how a team works.
Tonic Structural is built for this operational side. Its AI agent handles configuration so setup is measured in minutes rather than hours, and its on-demand provisioning and ephemeral environments let developers spin up fresh, safe datasets themselves and tear them down when a task is done. Wiring that generation into your delivery pipeline closes the loop — provisioning fresh, safe test data inside CI/CD pipelines means every environment gets current data automatically, which also removes the temptation to shortcut the process by copying production straight into a lower environment, a practice that quietly reintroduces the exact privacy risk the workflow exists to remove. A repeatable loop keeps the whole thing durable:
- De-identify production consistently, so no sensitive value reaches a lower environment.
- Subset to the size each environment actually needs, keeping relationships intact.
- Synthesize the cases and volumes production underrepresents.
- Provision on demand and refresh on a schedule, driven from the pipeline.
Run that way, test data stays safe, realistic, and repeatable at once — which is the whole job: data that behaves like production across every dimension without carrying production's risk, delivered reliably enough that teams building software and running testing and QA can depend on it.