Most test suites are generous to the code under test. They feed it the clean, central case — a straightforward name, a well-formed number, a date comfortably inside the allowed range — confirm the happy path works, and call it covered. It usually does work. The bugs, almost without exception, live everywhere else: at the boundaries, in the rare-but-legal shapes, and on the inputs your code was supposed to reject but quietly accepted.
The problem is that these inputs are exactly the ones random generation is least likely to produce. Draw a thousand names and you will get a thousand comfortable ones — the apostrophe surname, the single-letter given name, the person born on 29 February show up so rarely that your suite may never see them. Real Fake Data gives you two switches that stop leaving that to chance.
edge mode: the awkward-but-valid corners
Set `edge=true` and a generator returns only its hard cases — the boundary values and unusual-but-legal records that real data contains but random draws rarely surface:
- Names: very short ones (Jan Lis), second given names (Anna Maria), double-barrelled surnames (Kowalska-Nowak), and the apostrophes and diacritics that break naive form validation.
- Dates: leap days, month boundaries, and the youngest and oldest ages a filter allows.
- Addresses: the corners of the geography — buildings with letter suffixes, unusually long street names.
- Identifiers: the rare-but-valid digit patterns that a checksum accepts but a lazy regex does not.
This is the data that trips up a form built for the average case. A field that assumes every surname is a single capitalised word, a column sized for the mean name length, a date picker that never considered a leap year — edge mode finds all of them, on purpose, in a controlled and reproducible way.
invalid mode: exercise your rejection paths
The other half of robustness is refusing bad input cleanly. Set `invalid=true` and a generator returns records that are wrong on purpose — a checksum digit deliberately corrupted, a field malformed — while everything else about the record stays intact, so you can point the bad data at precisely the validator you mean to test:
{
"data": {
"name": "Magdalena",
"surname": "Wróbel",
"birthDate": "1995-09-02",
"pesel": "95090265041" // last digit wrong on purpose — must be rejected
}
}
// The test you have been missing:
const person = await realFakeData.plPerson({ seed: 7421, invalid: true });
expect(() => acceptCustomer(person)).toThrow(InvalidPeselError); A validator you never feed invalid data is a validator you have never actually tested. invalid mode lets you assert that bad input is rejected with the right error — not silently accepted, not met with an unhandled exception five frames deep, but caught and reported the way you intended.
Between them, edge and invalid turn "we tested the happy path" into "we tested what happens when the input fights back." That is where real confidence comes from.
Both flags compose with everything else — the same `seed` for reproducibility, the same `count` for bulk generation, the same country endpoints across the EU. You can seed a batch of a hundred edge-case people, or a table of records with exactly one broken checksum each, and know the set will be identical the next time you run it. The unhappy path stops being the part of the system you find out about from your users.