Test data methodology, privacy and safety

What the generator creates, how repeatability works and why generated values are designed for testing rather than real-world identity or payment use.

Generation happens locally

Fun Data Playground is a static application. Schema editing, preview generation and file export run in your browser; there is no server-side generation API, and the generated rows are never uploaded. Your current schema is stored in your own browser's localStorage so it survives a refresh. Signing in is entirely optional and adds only account sync: your schemas, datasets, history and settings replicate to Firebase Authentication and Cloud Firestore so they follow you to another device. Aggregate, anonymized visit analytics (Google Analytics) are collected separately from your generated data; the Content Security Policy scopes all outbound connections to those Google services and nothing else.

What is stored, and where

Everything below lives in your own browser unless you deliberately sign in:

  • fundata-schema-v1 — the schema you are currently editing, so a refresh does not lose it.
  • fundata-saved-schemas-v1, fundata-datasets-v1, fundata-history-v1 — named schemas, reusable custom datasets and recent generation history.
  • fundata-dark and fundata-consent — theme preference and your analytics answer.

Signing in replicates those same four data keys to a single Firestore document keyed to your account, so they follow you to another device. Generated rows are never part of that sync — only the schema that describes them. Creating a share link publishes that schema, and only that schema, to a document anyone with the link can read until you revoke it.

Analytics stay off until you accept them. Nothing is loaded from Google's tag servers before that, and declining is remembered, so the question is asked once rather than on every visit.

Deterministic seeds

A seed string is converted into the internal state of a deterministic pseudo-random number generator. Every field then consumes that sequence in schema order. The same seed, schema, options and row count produce byte-identical output. Changing field order or an option intentionally changes the sequence, so exported schemas are the safest way to preserve a long-lived fixture definition.

Concretely: the seed string is hashed to a 32-bit integer, which becomes the state of a small mulberry32 generator. That is a fast, well-distributed, non-cryptographic PRNG — the same family of algorithm used for procedural generation in games, chosen for exactly the property that matters here, which is that the sequence is completely determined by its starting state.

What changes the output, and what does not

Reproducibility is a contract, and it is worth knowing its edges. Output changes when you change the seed, add or remove a field, reorder fields, rename a field in a way that alters the export, change any field option, or change the row count. Output does not change with your browser, operating system, locale, time zone, or the time of day — the sequence carries no machine-specific input.

One consequence catches people out: leaving the seed empty is not "seed zero", it means an arbitrary seed per run. If you want the same file tomorrow, type something into the Seed field. Anything will do; the string itself is not secret and is worth committing next to the fixture.

The other consequence is a limit rather than a bug. A 32-bit seed space is roughly 4.3 billion distinct streams. That is far more than any fixture library needs and far too few to be treated as unpredictable — see the note on cryptographic use below.

Rows that hang together

Drawing every column independently produces rows that are individually plausible and collectively absurd: a customer called Marco Schneider whose email is elena.rossi@example.org, living in Paris, United States. Two field families are therefore resolved per row rather than per cell.

Person fields — First Name, Last Name, Full Name and Email Address — resolve to one underlying person for that row, so the email is built from the name beside it. Location fields — City, Country and Country Code — resolve to one place, so a city always sits in the country next to it.

Everything else is drawn independently, and that is a deliberate boundary rather than an oversight. Latitude and longitude are not the coordinates of the row's city; a postcode is a plausible shape, not that city's real postcode; and a manager_id or a foreign key drawn at random points nowhere in particular. Where a relationship has to hold, derive it — a Formula field for values computed from other columns, a Custom List seeded with real parent keys for references.

Fictional and documentation-safe values

  • Names, companies and addresses are assembled from reference lists and random combinations; they are not profiles copied from a customer database.
  • Email and domain values use reserved documentation domains such as example.com and .test.
  • IPv4 values come from the three RFC 5737 documentation ranges (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24) and IPv6 values from RFC 3849's 2001:db8::/32. None of them routes to a real host.
  • Domain names and URLs resolve to nothing: they are built on the reserved .test, .invalid and .example top-level domains, which cannot be registered.
  • Credit card values are the four published payment-gateway test PANs — see test card numbers. They are not issued accounts and are rejected by live payment processing.
  • IBAN values carry the correct country prefix and length but random check digits, so they fail the ISO 13616 mod-97 check by construction — see fake IBANs.
  • Passwords are drawn from an unambiguous character set with the same non-cryptographic generator as everything else. They are form-filling material, not credentials.
  • Avatar URLs point at the third-party placeholder service i.pravatar.cc. The generator only emits the URL as text — nothing is fetched here — but an application that loads those URLs will make real requests to that service.

The single exception worth calling out: phone numbers. There is no globally reserved phone range equivalent to example.com, so a number generated from a generic pattern can belong to a real subscriber. If anything downstream might dial or text your fixture, pin the pattern to a reserved fictional range — the phone number generator lists the common ones.

Never use this for anything that needs to be unpredictable

The generator is deterministic on purpose, which makes it exactly the wrong tool for any value whose security depends on being unguessable. That rules out session tokens, password-reset links, API keys, one-time codes, and anything else where an attacker guessing the next value is the threat. The UUID field is the one most likely to be misapplied here: the format is correct, the randomness is not cryptographic, and format is not the property that matters. Use your platform's crypto.randomUUID() or an equivalent CSPRNG for those.

Realism is not validation

Synthetic output is intended to exercise interfaces, serializers, imports and test logic. It does not guarantee postal deliverability, legal identity, bank ownership, phone reachability or production-grade statistical representativeness. Add application-specific constraints when your test requires them.

Statistical shape deserves a specific warning. Most fields draw uniformly across their range, and almost nothing in the real world is uniform — incomes, order values, session lengths and page views are all heavily skewed. A model, a capacity plan or a performance benchmark built on uniform data will be confidently wrong. The Number (Normal Dist.) field and weighted Custom Lists (repeat a value to make it more common) exist to close some of that gap, but generated data is a substitute for production data in tests, not in analysis.

Synthetic data and data protection

Data-protection regimes such as the GDPR and Türkiye's KVKK apply to personal data — information relating to an identifiable living person. Values produced here are not derived from any individual: names are recombined from public-domain pools, and no field is sampled, masked or perturbed from a real record. That is a meaningfully different position from anonymised or pseudonymised production data, where the source rows existed and re-identification is a question of how hard someone tries.

Two practical consequences. Generating from scratch avoids the re-identification problem rather than mitigating it, which is why it is a better answer than anonymising a production export for a demo environment. And a fixture you generate is safe to commit to a repository, paste into a bug report or hand to a contractor.

What this page cannot do is tell you whether your particular use is compliant — that depends on your jurisdiction, your data flows and how you combine this data with everything else. It is a description of how the generator works, not legal advice.

Testing boundaries deliberately

Use Blank % for nullable columns, Unique for collision-sensitive identifiers, sequential dates for time series, custom lists for domain states and patterns for controlled identifiers. A good fixture includes both ordinary rows and explicit edge cases rather than relying on randomness to discover them.

Randomness finds edge cases slowly and unreliably, and the ones that matter are usually specific: a name with an apostrophe, an empty optional field, a leap day, a value at the exact column width, an address in a country with no postal code. Generate the ordinary rows, then add those deliberately through a Custom List so a failure points at something nameable. The QA guide works through the data classes worth covering.

Ready to apply these rules? Browse the field type reference or choose a workflow from test-data use cases.

Common questions

Where is the data generated?

In your browser. There is no generation server, so a schema and the rows it produces never leave the machine you are on — not because the transfer is encrypted, but because there is no transfer.

Is the generated data random enough to be secure?

No, and it must not be used as though it were. Generation runs on a seeded pseudo-random number generator, which is precisely what makes output reproducible; that same property makes it predictable. Use a cryptographic source for tokens, keys, password resets or anything whose safety depends on being unguessable.

Are the generated emails, cards and IBANs real?

No. Emails use domains reserved by RFC 2606 and RFC 6761 that cannot receive mail, card numbers are the published payment-gateway test PANs rather than arbitrary Luhn-valid numbers, and IBANs have the right country length with random check digits, so they fail mod-97 validation by design.

Does generated data count as personal data under the GDPR?

Not when nothing in it derives from a real person, which is the case here: values are drawn from fixed public-domain reference lists and combined by a seeded generator, with no real record as input. The judgement is still yours to make for your own use, and combining synthetic columns with real ones produces a real dataset again.

Is realistic-looking data the same as valid data?

No, and treating it as such is the most common way this kind of tool misleads. A value can have the right shape and still fail every check a real system applies — a well-formed IBAN that fails its checksum, an address that no postal service recognises, a phone number in no assigned range.

Last updated