Test Data Generator, Explained

After reading this you will know how to build a fake dataset that behaves like real data in your tests, how to shape it as CSV or JSON, and which pitfalls make generated data leak, break parsers, or fail to catch the bug you were hunting.

What it does and one quick example

The Test Data Generator builds rows of realistic but invented data: names, emails, phone numbers, addresses, companies, dates, UUIDs and numbers. You pick the columns, set a row count, and export CSV or JSON. Nothing is looked up from a real directory. Every value is assembled from public word lists, so a row like this one is plausible but describes nobody:

Maria Alvarez,[email protected],+1-555-0182,Riverside

That single row already shows the point of the tool. You get data that looks like production so your code paths behave the same, without copying a single real customer record into your test environment.

When to use it, and when not to

Use generated data when you need volume, variety, or a safe stand-in for something private. A pagination bug that only appears past 10,000 rows is easy to reproduce when you can mint 10,000 rows in one click. A CSV importer that chokes on names with commas is easy to test when you can generate a batch full of edge cases.

Do not use it when the shape of your real data carries meaning you are trying to measure. If you are tuning a fraud model, random names and amounts will not reproduce the correlations that matter. Generated data is uniform and independent by construction. Real data has structure: repeat customers, clustered timestamps, skewed amounts. The generator gives you format realism, not statistical realism.

A good rule: use generated data to test that code runs correctly on well-formed input, and use small hand-built fixtures to test that it handles the specific weird cases you care about. The generator fills the first need cheaply.

How the values are built

Each column draws from a fixed pool of tokens. A name comes from a list of first names and a list of surnames. If the first-name list holds 200 entries and the surname list holds 500, the number of distinct full names is:

N = 200 \times 500 = 100{,}000

Here N is the count of unique combinations. That number sets a ceiling on variety. Ask for more rows than that and repeats become certain. Even well below the ceiling, repeats appear sooner than intuition suggests, because of the birthday problem.

The chance that all r rows are distinct, drawing uniformly from N possibilities, is:

P(\text{all distinct}) = \prod_{i=0}^{r-1} \left(1 - \frac{i}{N}\right)

Here r is the row count, N is the pool size, and the product multiplies the survival probability of each new draw. With N = 100{,}000 and r = 400, the probability of at least one collision is about 0.55. So a 400-row name column will probably contain a duplicate. Plan for that if your test assumes uniqueness.

Reproducing the demo data

The demo button uses the field defaults: a handful of columns and a small row count. Walk through what the generator does for one row so the output is not a black box.

Building one demo row

  1. Pick a first name from the first-name pool: Maria.
  2. Pick a surname from the surname pool: Alvarez.
  3. Derive the email by lowercasing and joining with a dot, then attaching the safe domain example.com: [email protected]. The example.com domain is reserved by standard and can never send or receive mail, so generated addresses cannot reach a real inbox.
  4. Pick a phone number in the reserved 555-01xx range: +1-555-0182. That range is set aside for fiction, so it dials nobody.
  5. Pick a city token: Riverside.

Concatenate with commas and you get the CSV line shown earlier. Ask for 5 rows and you get 5 independent draws, each following the same 5 steps.

The same 5 rows exported as JSON wrap each row in an object, which is easier for most code to consume without a CSV parser:

[{"name":"Maria Alvarez","email":"[email protected]"}]

Watching collisions grow with row count

The collision probability climbs fast, then flattens near certainty. The chart below plots the chance of at least one duplicate name against row count, for a pool of N = 100{,}000.

The 50% mark falls near 372 rows, far below the 100,000 pool size. Duplicates are normal, not a bug.

Read the curve at r = 300: the probability of a collision is about 0.36. By r = 700 it is about 0.91. If you need every value unique, do not rely on chance. Add a UUID column, which is drawn from a space of 2^{122} values and effectively never repeats at any practical row count.

With a pool of 100,000 unique values, the chance of at least one duplicate reaches 50% at about 372 rows and 90% at about 680 rows. Shrink the pool to 1,000 and the 50% point drops to about 38 rows.

Formats and encoding, byte by byte

CSV looks simple until a value contains a comma, a quote, or a newline. The convention (RFC 4180) is: wrap the field in double quotes, and double any internal quote. A company named Alvarez, Inc. must be written as:

"Alvarez, Inc."

A value containing a quote, like Bob "The Builder", becomes "Bob ""The Builder""". If your importer splits on raw commas without honoring quotes, that first field will break into two. Generating a batch that includes commas in company names is a direct way to catch that class of bug.

Encoding matters too. A name like Zoë holds the character ë (U+00EB), which in UTF-8 is two bytes: 0xC3 0xAB. Save the file as Latin-1 and re-open it as UTF-8 and you get Zoë. Export as UTF-8 without a byte order mark unless a downstream tool demands one; Excel on Windows sometimes needs the BOM (0xEF 0xBB 0xBF) to read UTF-8 correctly.

JSON is stricter than CSV. Numbers, booleans and null are bare tokens; everything else is a quoted string. A phone number like +1-555-0182 must be a string, not a number, or the leading plus and the hyphens make it invalid JSON. Keep identifiers, phone numbers and zip codes as strings even when they look numeric.

Common mistakes

The errors below waste the most time in practice.

Assuming uniqueness
As shown, a 400-row name column collides more often than not. Use a UUID column for keys that must be unique.
Treating numeric-looking fields as numbers
A zip code like 02134 loses its leading zero the moment it becomes an integer, turning into 2134. Keep it a string.
Testing only the happy path
Default output is well-behaved. Real input is not. Include long names, accented characters, and values with embedded commas so your parser meets them before your users do.
Shipping generated data as if it were random enough for secrets
These values come from small public word lists. They are fine for tests and useless as passwords, tokens, or cryptographic keys.

Related tools

Once you have a dataset, a few neighboring tools pair well with it. To turn generated contact rows into importable contact files, use the vCard Generator & Parser. To build calendar events from generated dates, use the iCalendar Generator & Parser. To visualize a numeric column you generated, paste it into the Chart Maker and export a PNG or SVG.

Frequently asked questions

Is any of this data real?

No. Every value is assembled from public sample word lists. Emails use the reserved example.com domain and phone numbers use the fictional 555-01xx range, so neither can reach a real person. Any match to a real individual is coincidence.

Does my data get uploaded anywhere?

No. Generation runs entirely in your browser. Nothing is sent to a server, so there is nothing to leak. Generated data cannot expose what it never contained.

How many rows can I generate?

The practical limit is your browser's memory. A 100,000-row CSV with 8 short columns is a few megabytes and generates in under a second on a typical laptop. If you need millions of rows, generate in batches and append.

Why do I keep seeing the same name twice?

Because the name pool is finite and draws are independent. With a pool near 100,000, a duplicate is more likely than not once you pass roughly 372 rows. Add a UUID column if you need guaranteed unique keys.

CSV or JSON, which should I pick?

Use CSV for spreadsheets and quick imports into databases. Use JSON when code consumes the data directly, since it avoids CSV quoting rules and keeps types explicit. For nested or optional fields, JSON is the safer choice.