Skip to main content
Handbook/Test/Page 63 · Test Data

Fake Data and Fake Services

Share

Share this page

Pass it to someone who needs it.

Star on GitHub

Key takeaway: Feed tests fake data, not the real thing

You have tests now, but a test needs something to run on. Some of your code also reaches out to the real world: a payment provider, an email sender, another API. A test must never charge a real card, send a real email, or depend on data that changes underneath it. This chapter gets you controlled test data and safe stand-ins for those outside services.

8.3.1Tests need their own data

A test can only check "did I get the right answer" if it knows the answer in advance. That means predictable, controlled input, never your real or production data, which changes the moment a user edits something.

Prepared test data like this is a fixture: a fixed, known record your test runs against. Your agent writes these; here is the shape:

export const testUser = { id: "u_1", email: "[email protected]", plan: "free", };

The test uses testUser and knows exactly what to expect, every run, forever.

8.3.2Make data with a factory, not by hand

Writing each fixture by hand gets tedious fast, and one change to the shape of your data breaks every one you typed out. When you need many records, or just realistic-looking ones, use a factory: a small function that builds fake data on demand.

A library like Faker fills in believable names, emails, and dates for you:

import { faker } from "@faker-js/faker"; export const makeUser = (over = {}) => ({ id: faker.string.uuid(), email: faker.internet.email(), ...over, });

Call makeUser() for a random one, or makeUser({ plan: "pro" }) to pin the one field a test cares about.

8.3.3Never call the real service in a test

Your code calls out to services you do not own: a card gets charged, an email gets sent, another API answers. In a test, none of that can happen. You cannot spend real money or send a real email a thousand times a day.

So you swap the real service for a mock: a stand-in that returns a canned response instead of doing the real thing. Tests stay fast, and they never touch the outside world.

In a test, calls to the outside world hit a mock that returns a canned answer, never the real service.

For code that talks to an API over HTTP, Mock Service Worker intercepts the request and hands back whatever you tell it:

http.post("https://api.stripe.com/v1/charges", () => HttpResponse.json({ id: "ch_test", status: "succeeded" }) );

The code under test thinks it charged a card. Nothing left your machine.

8.3.4Reset between tests, so they don't leak

A test that leaves data behind poisons the next one. Say test A creates a user and test B counts users. B passes only because A ran first. The day you run B alone, it fails for no reason you can see.

So every test starts from a clean slate. Reset the test database and any shared state before each one runs, so no test can depend on the leftovers of another. Your agent wires this into the test setup once.

You have one database, so where does the test one come from? Three options: a second database on the server you already run, a throwaway SQLite file for speed, or a fresh container per run. Use the same engine as production for anything testing SQL you actually rely on, and the fast throwaway one for everything else.

Then treat the connection string as a hard rule, because "reset before each test" means "delete everything". It points at the test database and nowhere else. Point it at your development or production database and the next test run wipes it.

Rule of thumb: any test must pass on its own, in any order. If it only passes after another test runs, something is leaking, reset it.

This prompt sets up test data and safe fakes for your stack:

Ready prompt
Act as a senior engineer setting up test data for my app. Read my data model and my vendor adapters first: fixtures follow the entities I already defined, and fakes replace my adapters, not the vendor SDKs behind them. Then do three things. First, add fixtures and factories: fixed records for known cases, a factory (Faker or my stack's equivalent) for realistic data on demand. Second, give each adapter a fake that returns canned responses, so no test touches a real payment, email, or outside API. Third, set up a separate test database, never my development or production one, and reset it and any shared state before each test, so tests never depend on each other and pass in any order. Report what you faked and what you reset, and add one line to my rules file: tests never call a real service. If you need the full reasoning behind this step, read https://zalt.me/guides/vibe-coding/test/test-data-and-mocking My app and stack:

Do this now: paste the prompt and name the outside service your app calls first. Let your agent replace it with a mock before another test ever risks the real thing.

Mahmoud Zalt

Mahmoud Zalt

Software engineer, 16+ yrs · built Sistava.com in 3 months, idea to production, using these methods

Resources
Star on GitHubContribute
Donate

Support my work

A small tip keeps the free work coming.

© 2026 Mahmoud Zalt. Free to read, not to republish.
Copyright & license