edgegoat Guides

Property based testing and curated edge cases in JavaScript

fast-check generates thousands of random inputs and shrinks failures to a minimal example. edgegoat hands you the few hundred inputs that already broke real code, with names and reasons. They answer different questions and work well together.

What each approach is good at

fast-checkedgegoatbig-list-of-naughty-strings
InputsRandom, generated from arbitrariesCurated, deterministicCurated raw strings
FindsUnknown bugs, with shrinkingKnown bug classes, every runString handling bugs
Explains whyNoYes, one line per caseComments by section
TypesAnyDates, zones, strings, numbers, URLs, paths, JSON, arraysStrings
DST and New Year instantsRarely hit by chanceComputed per zoneNo

The bug random inputs rarely find

A uniformly random instant falls inside a given one hour window about once in 8,760 tries, so a property test over random dates almost never lands on the hour where clocks jump. dates({ tags: ['dst'] }) hits it on every run, in every zone you choose. The same goes for a surrogate pair exactly at a cut boundary or a number between -1 and 0.

Seed fast-check with edgegoat values

example.test.js
import fc from 'fast-check';
import { test } from 'vitest';
import { strings } from 'edgegoat';

const tricky = fc.constantFrom(...strings().map((c) => c.value));
const text = fc.oneof(tricky, fc.string({ unit: 'grapheme' }));

test('slugify is idempotent', () => {
  fc.assert(fc.property(text, (s) => slugify(slugify(s)) === slugify(s)));
});

fast-check mixes the curated values with random ones and still shrinks failures. unit: 'grapheme' needs fast-check 3.22 or later.

Or keep the labels with check()

The same property, without a property testing library, reports failures by name and reason:

example.test.js
import { expect, test } from 'vitest';
import { check, strings } from 'edgegoat';

test('slugify is idempotent on every edge case', () => {
  const failures = check(slugify, strings(), (out) => slugify(out) === out);
  expect(failures.map((f) => `${f.label}: ${f.why}`)).toEqual([]);
});

FAQ

Instead of writing examples, you state a property that must hold for all inputs (for example "decode(encode(x)) equals x") and a library such as fast-check checks it against many generated inputs, shrinking any failure to a minimal case.

Both. Use fast-check to explore inputs you did not think of, and edgegoat to make sure the inputs that are known to break code are always tested, with readable names in the test report.

No. Every call returns the same cases, so failures are reproducible. DST instants are computed from the runtime tz database, so they are deterministic for a given Node.js version.

The big list is a large list of strings for fuzzing. edgegoat is smaller, explains every case, is filterable by tags and also covers dates, time zones, numbers, URLs, paths, JSON and arrays.

More guides