Mock data is a test input or substitute dependency, not automatically “safe fake user data.” A random name generator may create a plausible string but still violate a database constraint, send mail to a real address, or hide the edge case a test needs. Good test data is intentional: it represents a contract, a failure mode, or a distribution, and it can be regenerated or audited.
Key Takeaways
- Distinguish fixtures, stubs, fakes, simulators, factories, and synthetic datasets. They solve different test problems.
- Fake data is not anonymized production data. Synthetic records can still contain sensitive patterns, resemble real people, or be sent to external systems.
- Use reserved domains, documentation IP ranges, test payment numbers, and non-routable endpoints. Never generate secrets or credentials for authentication tests with a general faker.
- Preserve referential integrity, uniqueness, temporal order, state transitions, locale, and domain invariants.
- Pin generator, locale, timezone, seed, schema, and library versions when snapshots or reproducibility matter. A seed is reproducibility metadata, not a security secret.
- Randomness alone is weak coverage. Combine representative fixtures, boundary cases, invalid values, property-based generation, contract validation, and production-like load profiles.
Test Doubles and Data Strategies
| Strategy | Purpose | Typical control |
|---|---|---|
| Fixture | A reviewed, stable input/output | Checked-in versioned file |
| Factory | Creates valid records with overrides | Defaults plus explicit scenario parameters |
| Stub | Returns a predetermined dependency result | Request/argument matching |
| Fake | Lightweight working implementation | In-memory database or queue |
| Simulator | Models timing, faults, or external behavior | Controlled state machine |
| Synthetic dataset | Many records with chosen distributions | Generator, seed, provenance, quality checks |
| Anonymized data | Transformed real data with reduced linkability | Formal threat model and re-identification review |
Do not call a transformed production dump “mock data” and assume the privacy problem is solved. Anonymization and synthetic generation require separate threat models, access controls, retention rules, and deletion procedures.
Start with the Contract
Before generating records, write down:
- required and optional fields, types, ranges, and formats;
- uniqueness, foreign keys, and cross-record relationships;
- state transitions and valid event order;
- timezone, locale, currency, rounding, and calendar rules;
- error and authorization cases;
- fields that must never be emitted, persisted, or sent to a third party.
Validate generated data against the same JSON Schema, OpenAPI contract, database constraints, or domain validator used at the system boundary. A generator that emits syntactically valid JSON can still create an impossible business state.
Safe Value Domains
Use values that cannot accidentally contact real people or systems:
| Field | Safer test strategy |
|---|---|
[email protected] or an intercepted mail sink |
|
| Hostname/URL | https://api.example.test with outbound network blocked |
| IPv4/IPv6 | Documentation ranges such as 192.0.2.0/24, 198.51.100.0/24, 2001:db8::/32 |
| Phone | A provider's documented test numbers, not random real numbers |
| Payment card | Payment processor test PANs and test mode only |
| Password/token | Dedicated secret fixtures or generated secrets stored only in the test scope |
| Names/addresses | Clearly synthetic values or a reviewed local fixture |
| Images | Local assets with rights and deterministic dimensions |
Do not generate a realistic password and then use it as a JWT secret, API key, or encryption key. Do not let test URLs make network requests by default. Use network-deny policies and assert that outbound calls are intercepted.
Determinism and Reproducibility
A reproducible dataset needs more than a numeric seed. Record the generator and dependency versions, locale, timezone, reference date, schema revision, seed, generation options, and source commit:
import { faker } from "@faker-js/faker";
faker.seed(20260206);
faker.setDefaultRefDate("2026-02-06T00:00:00.000Z");
const user = {
id: "user-0001",
name: faker.person.fullName(),
email: "[email protected]",
createdAt: faker.date.past({ years: 2 }).toISOString(),
};
console.log(user);
The exact API and output can change between library versions. Pin the version and do not use snapshots as the only assertion if generated data is intentionally allowed to evolve.
Seeds must not be treated as secrets. A predictable seed is useful for tests and unsafe for security tokens, password material, session identifiers, or cryptographic keys.
Factories and Invariants
Factories should make valid defaults easy and invalid cases explicit:
type User = {
id: string;
email: string;
status: "active" | "suspended";
createdAt: string;
};
let sequence = 0;
export function buildUser(
overrides: Partial<User> = {},
): User {
sequence += 1;
return {
id: `user-${sequence.toString().padStart(4, "0")}`,
email: `user-${sequence}@example.test`,
status: "active",
createdAt: "2026-02-06T00:00:00.000Z",
...overrides,
};
}
const suspended = buildUser({ status: "suspended" });
For relational data, create parents before children, retain foreign-key references, and reset state between tests. For time-dependent tests, inject a clock instead of calling the wall clock inside the factory. For concurrent tests, avoid global mutable counters or isolate each test's generator state.
Boundary and Property-Based Cases
A realistic average record is not enough. Deliberately include:
- empty, minimum, maximum, and over-limit strings;
- Unicode combining marks, RTL text, emoji, and mixed scripts;
- zero, negative, large, fractional,
NaN/infinite rejection where relevant; - missing,
null, duplicate, unknown, and reordered fields; - leap days, DST transitions, timezone offsets, stale and future timestamps;
- duplicate IDs, orphaned references, conflicting states, and retry/idempotency cases;
- unauthorized tenants, malformed tokens, timeouts, partial failures, and rate limits.
Property-based generators can explore combinations, but every failing seed must be captured as a regression fixture. Random exploration should complement, not replace, named examples that explain the business rule.
API and Database Testing
For API tests, separate response fixtures from request factories and assert status, headers, schema, authorization, error shape, and side effects. A successful HTTP status does not prove the response or database state is correct.
For database tests:
- generate records through the domain constraints or validate before insertion;
- use isolated databases or transactions;
- create realistic indexes and cardinalities for performance tests;
- avoid using a tiny random dataset to claim production query behavior;
- scrub or destroy test data according to retention policy.
Performance datasets should document row count, distribution, skew, hot keys, concurrency, query mix, hardware, cache state, and measurement date. “Generate a million random rows” is not a workload model.
Locale, Text, and Lorem Ipsum
Locale-aware generators are useful for UI layout and internationalization, but a library locale is not a guarantee of culturally valid names, addresses, phone formats, or legal data. Test long translations, missing glyphs, plural rules, date/number formats, RTL layout, and script changes.
Lorem Ipsum approximates Latin-looking density, not real language behavior. It does not test translation length, screen-reader meaning, search, copy/paste, content moderation, or right-to-left layout. Use reviewed localized strings and explicit length distributions for those tests. If placeholder text is decorative, label it so it is not announced as meaningful content.
Faker Example with Validation
Libraries such as Faker are convenience generators, not schema validators:
from dataclasses import dataclass
from datetime import datetime, timezone
@dataclass(frozen=True)
class User:
user_id: str
email: str
created_at: datetime
def build_user(index: int) -> User:
if index < 1:
raise ValueError("index must be positive")
return User(
user_id=f"user-{index:04d}",
email=f"user-{index:04d}@example.test",
created_at=datetime(2026, 2, 6, tzinfo=timezone.utc),
)
Add an independent schema/domain validation step and tests for invalid overrides. Do not assume that a library's “valid-looking” phone, card, URL, or address is safe to send or legally meaningful.
Mock Servers and Failure Simulation
A mock server should model the API contract and controlled failures, not just return random JSON. Define response fixtures for:
- success, empty, partial, and paginated results;
- schema-invalid and unknown-field responses;
- authentication failure, authorization denial, rate limiting, timeout, retry, and cancellation;
- duplicate delivery, out-of-order events, and idempotency conflicts.
Keep network calls deterministic in unit tests. Use integration or staging environments for real protocol behavior, with isolated credentials and explicit data deletion.
Privacy and Governance
Synthetic data can still be sensitive if it is derived from production distributions, contains copied values, or is sent to a third-party generator. Maintain an inventory of datasets and artifacts, restrict access, encrypt where required, scan logs and snapshots, and document retention and deletion.
Never place real credentials, access tokens, private keys, personal identifiers, or production database exports in fixtures. A test repository is still a data boundary.
Frequently Asked Questions
Is mock data always privacy-safe?
No. Purely synthetic values reduce exposure, but generators can emit real-looking contact data, copied templates, or sensitive distributions. Use reserved domains and test values, block network delivery, and review provenance.
Should a seed be kept secret?
No. A seed is reproducibility metadata. It must never be used as password, token, key, or entropy for a security primitive.
Can random mock data replace edge-case tests?
No. Random data may miss rare combinations and does not document the expected invariant. Use named boundary fixtures and property-based tests together.
Can mock data be used in production?
Only when it is an intentional product behavior, such as an empty-state demo with explicit labeling. It must not silently replace real business data, authorization decisions, billing records, or audit evidence.
How do I test payment fields safely?
Use the payment provider's documented test mode and test card numbers, isolate credentials, and assert that the application never sends test data to a live endpoint. Do not invent numbers and call them valid payment instruments.
Is Lorem Ipsum good for UI testing?
It is useful for rough visual density in Latin-oriented layouts, but it does not test localization, semantics, accessibility, search, moderation, or RTL behavior. Use representative localized fixtures for those requirements.
Primary Sources
- RFC 2606: Reserved Top-Level DNS Names
- RFC 5737: IPv4 Documentation Ranges
- RFC 3849: IPv6 Documentation Address Prefix
- OWASP: Sensitive Data Exposure Prevention
- W3C Web Content Accessibility Guidelines (WCAG) 2.2
- Faker.js documentation
Conclusion
High-quality mock data is a controlled test instrument, not a pile of plausible random records. Define the contract, preserve relationships and invariants, use reserved values, pin reproducibility metadata, generate named failures, validate independently, and govern every dataset and artifact. That approach improves test signal without importing privacy, delivery, or security risk.