Generate Mock Data for Frontend Testing: Seeds, MSW and Edge Cases
Frontend work needs data long before the API is ready, and tests need data that behaves the same on every run. Fake data covers both, but only if it is realistic enough to expose layout bugs, stable enough for snapshots, and free of anything real. "John Doe, test@test.com" repeated fifty times meets none of those goals. The sections below generate mock data that does: seeded, full of awkward values, and unable to reach a real inbox.
Generate realistic data with a seed
Libraries such as Faker generate plausible names, addresses, dates, prices and text in dozens of locales. The one habit that matters is seeding: with a fixed seed, the generator returns the same data on every run, so screenshots, snapshot tests and bug reproductions stay stable.
import { faker } from '@faker-js/faker';
faker.seed(42); // same data on every run
const users = Array.from({ length: 20 }, () => ({
id: faker.string.uuid(),
name: faker.person.fullName(),
email: faker.internet.email({ provider: 'example.com' }),
avatar: faker.image.avatar(),
joinedAt: faker.date.past({ years: 2, refDate: '2026-01-01' }).toISOString(),
plan: faker.helpers.arrayElement(['free', 'pro', 'team']),
}));
Pass a fixed refDate for anything relative to "now". Otherwise the seed stays the same but the dates move every day, and date-based snapshots break. For the same reason, freeze the clock in tests (vi.useFakeTimers(), jest.useFakeTimers() with setSystemTime) when components show relative times such as "3 days ago".
Python has Faker with Faker.seed(42), PHP has fakerphp/faker (Laravel factories use it), and Ruby has the faker gem. For quick one-off datasets as JSON or CSV, an online mock data generator avoids writing code at all.
Mock the API, not the component
Hard-coding arrays inside components hides the real behaviour: loading states, errors, pagination and slow responses. Mocking at the network level keeps components unchanged and exercises the real fetch code. Mock Service Worker (MSW) intercepts requests in the browser and in Node, so the same handlers serve local development, unit tests and Storybook.
import { http, HttpResponse, delay } from 'msw';
export const handlers = [
http.get('/api/users', async ({ request }) => {
const page = Number(new URL(request.url).searchParams.get('page') ?? 1);
await delay(400); // realistic latency
return HttpResponse.json({ data: users.slice((page - 1) * 10, page * 10), page });
}),
http.post('/api/users', () =>
HttpResponse.json({ error: 'Email already taken' }, { status: 409 })),
];
Other options are JSON Server, which gives a REST API from a JSON file in one command, and mocks generated from an OpenAPI specification, which stay in step with the real contract. Whichever you use, include failure responses (401, 409, 500, timeouts). Error handling is the part of a front end most often never seen before production.
Edge-case data that finds layout bugs
Random realistic data mostly produces average values. Bugs live in the extremes, so add a fixed set of awkward records alongside the random ones.
const edgeCases = [
{ name: 'Wolfeschlegelsteinhausenbergerdorff Hubert Blaine', email: 'a.very.long.address+newsletter@subdomain.example.com' },
{ name: 'Ó Súilleabháin', email: 'ó@example.com' }, // accents; non-ASCII local part
{ name: 'محمد بن سلمان', email: 'rtl@example.com' }, // right-to-left script
{ name: '李小龍', email: 'cjk@example.com' }, // CJK, no spaces
{ name: 'Zoë 👩💻', email: 'emoji@example.com' }, // emoji with ZWJ sequence
{ name: '', email: 'noname@example.com' }, // empty
{ name: '<b>Bold</b> & "quotes"', email: 'html@example.com' }, // must render as text
];
Also test empty states (zero results), one item, exactly one page and one more than a page, very large numbers and prices with many digits, negative values, long unbroken words and URLs that overflow containers, and dates around daylight-saving changes and year boundaries. For lists, try a thousand rows to see whether virtualisation or pagination is needed.
Keep this set deterministic and named, so a failing screenshot says "long-name user" rather than "random user #17".
Use values reserved for testing
Fake data should never hit a real person. Generated emails at real domains can receive password-reset or notification emails if the data leaks into a system that sends mail, and generated phone numbers can belong to someone.
email domains example.com, example.org, example.net, *.test (RFC 2606, RFC 6761)
IP addresses 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24 (RFC 5737)
US phones 555-0100 to 555-0199 (reserved for fictional use)
UK phones 07700 900000 to 07700 900999 (Ofcom drama numbers)
cards provider test numbers, e.g. Stripe's 4242 4242 4242 4242
Configure your generator to use these ranges. The provider option in the Faker example above does this for emails.
Never copy production data into test fixtures
It is tempting to export a few real customers to make a demo look right. Under GDPR and similar laws that is processing personal data for a new purpose, and fixtures end up in repositories, screenshots, CI logs and bug trackers where access is not controlled. If realistic distributions matter, generate synthetic data that matches their shape, or anonymise properly: replace every identifying field, not only names, because email, address and date of birth together identify people just as well.
Keep mocks honest
Mock data drifts from the real API: a field is renamed, a nullable becomes required, a list gains pagination. Validate mock responses against the same schema as the real API, whether OpenAPI, JSON Schema or a Zod or TypeScript type shared with the client, so drift fails a test instead of failing in production. Periodically run the front end against the real staging API as well. Mocks prove that your code handles the data you imagined, not the data you will actually get.
Sources and further reading
The Faker.js and MSW documentation cover seeding, refDate, handlers and delays. RFC 2606 and RFC 6761 reserve example.* and .test, and RFC 5737 reserves documentation IP ranges. The North American Numbering Plan administrator reserves 555-0100 to 555-0199 for fictional use, and Ofcom publishes UK numbers reserved for drama. Stripe documents its test card numbers.
Frequently asked questions
What is a mock data generator?
A tool or library that creates realistic but fictional records, such as names, emails, addresses, dates and prices, for development, demos and tests. Faker is the most common library; online generators export the same kind of data as JSON or CSV.
How do I make fake data the same on every test run?
Set a fixed seed, for example `faker.seed(42)`, and pass a fixed reference date for anything relative to now. Freeze the clock in tests that display relative times.
What email addresses are safe for test data?
Addresses at `example.com`, `example.org`, `example.net` or a `.test` domain, which are reserved by RFC 2606 and never deliver to real people.
What is the difference between mock data and a mock API?
Mock data is the fake records themselves. A mock API, for example with MSW or JSON Server, serves those records over HTTP so the front end runs its real fetch, loading and error code against them.
Can I use real customer data for testing?
Avoid it. Copying personal data into test fixtures and staging systems usually lacks a legal basis, and those environments are less protected. Generate synthetic data or anonymise every identifying field.
What edge cases should fake data include?
Very long and empty names, non-Latin and right-to-left scripts, emoji, HTML-like text, zero and one items, page boundaries, large numbers and dates around daylight-saving changes. Keep them as a fixed, named set.