Random Numbers, Names and Emails for Test Data, Done Properly
Unbiased random ranges, why Math.random is not for raffles, names that break forms in useful ways, and email domains that can never reach a real inbox.
· 6 min read

A staging server once sent 4,000 password-reset emails to addresses like john.smith@gmail.com, because someone seeded the database with "fake" users on real domains. A raffle script picked winners with Math.floor(Math.random() * n) and nobody could prove it was fair. A checkout form shipped with a name field that rejected "O'Brien". All three are test-data problems, and all three have simple fixes once you know the failure mode.
This article covers the three generators that most often feed test data and quick decisions: random numbers, random names and random email addresses. Each section shows what can go wrong and how the matching tool avoids it.
Random numbers without bias
The Random Number Generator picks one whole number between a minimum and a maximum, both included. Two properties matter more than they look: where the randomness comes from, and how it is mapped onto your range.
Where the bits come from
JavaScript's Math.random() is a pseudo-random generator. In V8 (Chrome, Node.js, Edge) it has been xorshift128+ since 2015: fast, statistically decent, and predictable. Given enough consecutive outputs, its internal state can be recovered and every future value computed. That is fine for shuffling a game board. It is not fine when the number decides who wins a prize, which account gets audited, or what a token contains.
crypto.getRandomValues() draws from the operating system's cryptographically secure generator, the same source used for TLS keys. It is fast enough for anything a person clicks, so the tool uses it for every draw.
Mapping bits onto a range: modulo bias
The tempting code is:
const n = crypto.getRandomValues(new Uint32Array(1))[0];
return min + (n % (max - min + 1)); // biased
A 32-bit draw has 4,294,967,296 equally likely values. If the range size does not divide that number evenly, the remainder is spread unevenly: a few results get one extra way to be chosen. For a range of 3, the effect is about one part in a billion. For a range just above 2^31 (a little over two billion), almost half the results become twice as likely as the others.
The standard fix is rejection sampling, used by Java's nextInt(bound) and Python's secrets.randbelow:
function randomIntInclusive(min, max) {
const range = max - min + 1;
const limit = Math.floor(2 ** 32 / range) * range; // largest multiple of range
let x;
do x = crypto.getRandomValues(new Uint32Array(1))[0];
while (x >= limit); // discard the uneven tail
return min + (x % range);
}
Every accepted value maps to each result the same number of times. In the worst case slightly under half the draws are thrown away, which still averages fewer than two draws per number. The tool extends this to ranges up to 2^53 by combining two 32-bit draws.
A worked example
- Click the 1-100 preset, or type 1 and 100.
- Click Generate Random Number.
- The Number Details panel shows Total possibilities: 100. Both 1 and 100 can come up.
Run it ten times and you will probably see a repeat. That is expected: the chance of ten distinct values from 1 to 100 is about 63%, so roughly one run in three contains a duplicate. People are bad at judging randomness and tend to see repeats as suspicious. If you need several unique picks, a raffle with three winners for example, draw from a list without replacement using the Random Picker rather than calling a number generator repeatedly.
Random names that test real cases
The Random Name Generator builds full names from six pools (Mixed, American, Hispanic, Asian, European and African), with optional middle names and suffixes, up to 1,000 at a time. You can copy them all or download a text file with one name per line.
The point of realistic names in test data is not realism for its own sake. Placeholder values like Test User 1 never exercise the code paths that real names break:
| Name feature | Example from the pools | What it catches |
|---|---|---|
| Accented letters | García, Traoré, Françoise | Collation, CSV encoding, PDF fonts |
| Spaces in surnames | De Luca | Splitting on the first space |
| Very short parts | Ma, Bah, Yu | Minimum-length validation |
| Suffixes | Jr., III, PhD | Suffix stored in last name, sorting |
| Common collisions | John Smith | Uniqueness constraints on names |
Patrick McKenzie's 2010 essay "Falsehoods Programmers Believe About Names" lists dozens more assumptions that fail in production, from "people have exactly one first name" to "names are written in one script". A generated batch will not cover them all, so add a handful of hand-written cases: an apostrophe (O'Brien), a hyphen (Smith-Jones), a single-word name, and a non-Latin script.
Two practical notes. First, names are drawn independently from a few dozen options per pool, so a list of a few hundred will repeat names; run it through Remove Duplicate Lines if you need unique values. Second, common combinations will match real people. That is harmless in a test database, but avoid pairing a generated name with invented claims in a public demo.
Fake email addresses that cannot reach anyone
The Random Email Generator produces addresses like swiftmoon42@example.com: an adjective, a noun and a number from 1 to 999, on a domain reserved for testing.
Reserved domains are the important part. RFC 2606, published in 1999, set aside example.com, example.net and example.org for documentation, and reserved the top-level domains .test, .example, .invalid and .localhost. RFC 6761 (2013) describes how software and DNS should treat them. IANA runs the example domains with a placeholder web page and no mail server, and .test will never be delegated in the public DNS.
Compare that with the domains people make up. test.com, demo.com, email.com and sample.org are all registered, and some accept mail. gmail.com addresses with random letters frequently belong to someone. Seeding staging with those means every password reset, invoice or newsletter your staging environment sends can land in a stranger's inbox, along with whatever data the email contains.
The generator uses eight reserved domains: example.com, example.org, example.net, mail.example.com, users.example.org, inbox.test, qa.test and staging.test. With 12 adjectives, 12 nouns, 999 numbers and 8 domains there are about 1.15 million combinations, so repeats are rare in small batches and likely in very large ones. Add a sequence number when uniqueness matters.
Two limits worth knowing:
- You cannot receive mail at these addresses. For sign-up flows that need a confirmation link, point your app's SMTP settings at a local catcher such as Mailpit, which captures every message in a web inbox.
- Some validators reject
.test. Libraries that check against a list of public TLDs or look up MX records will refuse reserved domains. Regenerate until you get anexample.*address, or relax the check in test environments.
Common mistakes
- Using
Math.random()for anything with stakes. Usecrypto.getRandomValuesor your language'ssecretsmodule. - Using
%to fit a random value into a range. Use rejection sampling or a library function that does it. - Assuming fair randomness avoids repeats. It does not. Draw without replacement when you need unique results.
- Seeding test data with real domains. Use example.com, example.org, example.net or
.test. - Testing names with ASCII only. Include accents, apostrophes, spaces and short names.
- Using real card numbers in test data. Use generated test numbers from the Test Credit Card Generator or your payment provider's sandbox list.
Combine the three generators with the UUID Generator for IDs and you have a seed file that looks like production, breaks in the places production breaks, and can never email, charge or identify a real person.