Generating Fake Test Data for Development and QA
June 5, 2026 · 4 min read
Testing with real user data is a privacy risk and often a compliance violation. Testing with no data means your tests don't reflect reality. Fake data threads the needle: realistic-looking records that let you test properly without exposing anyone's information.
This guide covers strategies for generating fake test data — from quick one-liners to seeded generators for reproducible test suites.
Why fake data matters
Real data causes real problems in test environments:
- Privacy regulations (GDPR, CCPA, HIPAA) restrict where real personal data can be stored and who can access it. Dev laptops and staging servers often don't qualify.
- Security surface — a breach of your test database that contains real emails and passwords is still a breach.
- Test isolation — real data changes. A test that passes today may fail tomorrow because a real user's email format changed.
Fake data is deterministic (when seeded), portable, and risk-free.
What "realistic" means for test data
Realistic fake data is structurally valid:
- Email addresses have the right format (
[email protected]) - Phone numbers match the expected pattern for the target locale
- Addresses reference real city names, not
"city123" - Names don't include characters that break your name-parsing logic
It doesn't need to correspond to real people — it just needs to not break your validation.
Browser-based fake data generation
The Fake Data Generator produces records in JSON or CSV format, with fields you select:
- id, name, email, phone
- age, job, company
- address, city, state, zip
- username
All data is generated deterministically using a seeded PRNG (no Math.random() — seed-based Math.sin(seed)), so the same settings always produce the same records. This is important for reproducible tests.
Seeded generation for reproducible tests
Randomness in tests is dangerous — a test that passes with one random dataset and fails with another is flaky. Use a seed:
// Simple seeded PRNG (good enough for fake data, not for crypto)
function seededRand(seed, max) {
return Math.abs(Math.sin(seed) * max | 0) % max;
}
const NAMES = ["Alice", "Bob", "Carol", "David", "Emma"];
function fakeName(recordIndex) {
return NAMES[seededRand(recordIndex * 137, NAMES.length)];
}
// Same index always produces same name
fakeName(0); // → "Alice" every time
fakeName(1); // → "Emma" every time
This approach requires no external library and works in any JavaScript environment.
Generating valid emails
The simplest approach: ${firstName.toLowerCase()}.${lastName.toLowerCase()}@example.com. The example.com domain is IANA-reserved for documentation and testing — it will never resolve to a real service, so test emails sent there are inherently safe.
function fakeEmail(first, last, index) {
const domains = ["example.com", "test.local", "qa.internal"];
const domain = domains[index % domains.length];
return `${first.toLowerCase()}.${last.toLowerCase()}${index}@${domain}`;
}
Generating valid phone numbers
US format: +1-NXX-NXX-XXXX where N is 2–9 (first digit of area code and exchange can't be 0 or 1):
function fakePhone(seed) {
const area = seededRand(seed, 800) + 200; // 200–999
const exchange = seededRand(seed + 1, 800) + 200;
const subscriber = seededRand(seed + 2, 9000) + 1000;
return `+1-${area}-${exchange}-${subscriber}`;
}
Generating addresses
Combine arrays:
const STREETS = ["Main St", "Oak Ave", "Maple Dr", "Cedar Ln"];
const CITIES = ["Portland", "Austin", "Denver", "Chicago"];
function fakeAddress(seed) {
const num = seededRand(seed, 9000) + 100;
const st = STREETS[seededRand(seed + 1, STREETS.length)];
const city = CITIES[seededRand(seed + 2, CITIES.length)];
return `${num} ${st}, ${city}`;
}
Generating IDs
For UUIDs in tests, use the UUID Generator or the Web Crypto API:
function fakeUuid() {
return crypto.randomUUID(); // supported in all modern browsers
}
For sequential IDs: i + 1. For ULIDs: ULID Generator.
Generating passwords for test accounts
Never use real passwords in test data. Generate random strings and hash them immediately:
import bcrypt from 'bcryptjs';
async function fakeHashedPassword(seed) {
// Deterministic "random" password
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
const password = Array.from({ length: 16 }, (_, i) =>
chars[seededRand(seed + i, chars.length)]
).join('');
return bcrypt.hash(password, 10);
}
Use the Bcrypt Generator to pre-compute hashes for fixed test accounts.
Output formats
JSON (for API testing and seeding)
[
{ "id": 1, "name": "Alice Smith", "email": "[email protected]" },
{ "id": 2, "name": "Bob Johnson", "email": "[email protected]" }
]
Import directly into API integration tests or seed scripts.
CSV (for spreadsheet imports and database bulk load)
id,name,email,phone
1,Alice Smith,[email protected],+1-203-456-1001
2,Bob Johnson,[email protected],+1-415-789-2002
Load with COPY (PostgreSQL), LOAD DATA (MySQL), or a spreadsheet import.
Privacy-safe sharing
When you need to share a dataset for a bug report or demo:
- Replace real names/emails with fake ones from the generator
- Keep all other field values (amounts, dates, status flags) — these are usually the interesting part
- Use the same seed so you can regenerate the same "sanitized" dataset
The Fake Data Generator generates output client-side — no data is uploaded or logged.
When to use the browser tool vs. a library
| Situation | Use |
|---|---|
| Quick demo or one-off seed | Browser Fake Data Generator |
| Automated test suite | Seeded code function (reproducible) |
| Large datasets (10k+ rows) | faker.js, @faker-js/faker, or similar |
| Locale-specific data (e.g. Japanese names) | @faker-js/faker with locale setting |
For format conversion on the output, use JSON Formatter or CSV to JSON.