Property based testing framework for JavaScript (like QuickCheck)
npx @tessl/cli install tessl/npm-fast-check@4.5.0Property-based testing framework for JavaScript and TypeScript. Automatically generates test cases from specifications to discover edge cases and bugs.
npm install fast-check --save-devES Module:
import fc from 'fast-check';
// Or import specific exports
import { assert, property, integer, string } from 'fast-check';CommonJS:
const fc = require('fast-check');
// Or destructure specific exports
const { assert, property, integer, string } = require('fast-check');Write your first property test:
import fc from 'fast-check';
// Test that string concatenation is associative
fc.assert(
fc.property(fc.string(), fc.string(), fc.string(), (a, b, c) => {
return (a + b) + c === a + (b + c);
})
);Property-based testing verifies that a property holds true for all generated inputs, rather than testing specific examples.
/**
* Create a synchronous property from arbitraries and a predicate
*/
function property<Ts extends [unknown, ...unknown[]]>(
...args: [...arbitraries: { [K in keyof Ts]: Arbitrary<Ts[K]> }, predicate: (...args: Ts) => boolean | void]
): IPropertyWithHooks<Ts>;
/**
* Create an asynchronous property from arbitraries and an async predicate
*/
function asyncProperty<Ts extends [unknown, ...unknown[]]>(
...args: [...arbitraries: { [K in keyof Ts]: Arbitrary<Ts[K]> }, predicate: (...args: Ts) => Promise<boolean | void>]
): IAsyncPropertyWithHooks<Ts>;/**
* Run a property and throw on failure
*/
function assert<Ts>(
property: IProperty<Ts> | IAsyncProperty<Ts>,
params?: Parameters<Ts>
): void | Promise<void>;
/**
* Run a property without throwing, returns detailed execution results
*/
function check<Ts>(
property: IProperty<Ts> | IAsyncProperty<Ts>,
params?: Parameters<Ts>
): RunDetails<Ts> | Promise<RunDetails<Ts>>;Basic Usage:
import { assert, property, integer, string } from 'fast-check';
// Synchronous property
assert(
property(integer(), string(), (num, str) => {
return str.repeat(num).length === str.length * num;
}),
{ numRuns: 1000 }
);
// Asynchronous property
await assert(
asyncProperty(integer(), async (num) => {
const result = await asyncOperation(num);
return result > 0;
})
);Skip test cases that don't meet requirements:
/**
* Add pre-condition checks inside property execution
*/
function pre(expectTruthy: boolean): asserts expectTruthy;Usage:
import { assert, property, integer, pre } from 'fast-check';
assert(
property(integer(), integer(), (a, b) => {
pre(b !== 0); // Skip cases where b is 0
return (a / b) * b === a;
})
);Arbitraries are data generators that produce random values and shrink failing cases to minimal counterexamples.
Generate basic JavaScript values: booleans, numbers, strings, dates.
function boolean(): Arbitrary<boolean>;
function integer(constraints?: IntegerConstraints): Arbitrary<number>;
function nat(constraints?: NatConstraints): Arbitrary<number>;
function float(constraints?: FloatConstraints): Arbitrary<number>;
function double(constraints?: DoubleConstraints): Arbitrary<number>;
function bigInt(constraints?: BigIntConstraints): Arbitrary<bigint>;
function string(constraints?: StringConstraints): Arbitrary<string>;
function date(constraints?: DateConstraints): Arbitrary<Date>;
function falsy(constraints?: FalsyContraints): Arbitrary<FalsyValue>;Quick Examples:
import { integer, string, boolean, date } from 'fast-check';
// Integers in range
integer({ min: 0, max: 100 })
// Strings with length constraints
string({ minLength: 5, maxLength: 10 })
// Dates in range
date({ min: new Date('2020-01-01'), max: new Date('2025-12-31') })Detailed Primitive Arbitraries
Generate arrays, sets, maps, tuples, and typed arrays.
function array<T>(arb: Arbitrary<T>, constraints?: ArrayConstraints): Arbitrary<T[]>;
function uniqueArray<T>(arb: Arbitrary<T>, constraints?: UniqueArrayConstraints<T>): Arbitrary<T[]>;
function set<T>(arb: Arbitrary<T>, constraints?: SetConstraints): Arbitrary<Set<T>>;
function map<K, V>(keyArb: Arbitrary<K>, valueArb: Arbitrary<V>, constraints?: MapConstraints): Arbitrary<Map<K, V>>;
function tuple<Ts extends unknown[]>(...arbs: { [K in keyof Ts]: Arbitrary<Ts[K]> }): Arbitrary<Ts>;
function subarray<T>(originalArray: T[], constraints?: SubarrayConstraints): Arbitrary<T[]>;Quick Examples:
import { array, tuple, set, map } from 'fast-check';
// Arrays
array(integer(), { minLength: 1, maxLength: 10 })
// Tuples
tuple(string(), integer(), boolean())
// Sets
set(integer(), { minLength: 5 })
// Maps
map(string(), integer(), { maxKeys: 10 })Detailed Collection Arbitraries
Generate objects, records, dictionaries, and JSON.
function record<T, K extends keyof T = keyof T>(
model: { [K in keyof T]: Arbitrary<T[K]> },
constraints?: RecordConstraints<K>
): Arbitrary<RecordValue<T, K>>;
function object(constraints?: ObjectConstraints): Arbitrary<Record<string, unknown>>;
function dictionary<T>(
keyArb: Arbitrary<string>,
valueArb: Arbitrary<T>,
constraints?: DictionaryConstraints
): Arbitrary<Record<string, T>>;
function json(constraints?: JsonSharedConstraints): Arbitrary<string>;
function jsonValue(constraints?: JsonSharedConstraints): Arbitrary<JsonValue>;Quick Examples:
import { record, json, jsonValue } from 'fast-check';
// Typed records
record({
id: string(),
age: integer({ min: 0, max: 120 }),
active: boolean()
}, { requiredKeys: ['id'] })
// JSON strings
json({ maxDepth: 3 })
// JSON values
jsonValue({ maxDepth: 3 })Generate URLs, emails, IP addresses, domains, UUIDs.
function webUrl(constraints?: WebUrlConstraints): Arbitrary<string>;
function emailAddress(constraints?: EmailAddressConstraints): Arbitrary<string>;
function domain(constraints?: DomainConstraints): Arbitrary<string>;
function ipV4(): Arbitrary<string>;
function ipV6(): Arbitrary<string>;
function uuid(constraints?: UuidConstraints): Arbitrary<string>;
function ulid(): Arbitrary<string>;Quick Examples:
import { webUrl, emailAddress, uuid } from 'fast-check';
// URLs
webUrl({ validSchemes: ['http', 'https'] })
// Email addresses
emailAddress()
// UUIDs
uuid({ version: 4 })Inspect generated values during development:
/**
* Generate an array of sample values from an arbitrary
*/
function sample<Ts>(
generator: IRawProperty<Ts> | Arbitrary<Ts>,
params?: Parameters<Ts> | number
): Ts[];
/**
* Gather and print statistics about generated values
*/
function statistics<Ts>(
generator: IRawProperty<Ts> | Arbitrary<Ts>,
classify: (v: Ts) => string | string[],
params?: Parameters<Ts> | number
): void;Usage:
import { sample, statistics, integer } from 'fast-check';
// Generate samples
const samples = sample(integer({ min: 0, max: 100 }), 10);
console.log(samples);
// Generate statistics
statistics(integer({ min: 0, max: 100 }), (n) => {
if (n < 25) return 'low';
if (n < 75) return 'medium';
return 'high';
});Configure test execution parameters globally or per-property.
function configureGlobal(parameters: GlobalParameters): void;
function readConfigureGlobal(): GlobalParameters;
function resetConfigureGlobal(): void;Common Parameters:
interface Parameters<T> {
seed?: number; // Random seed for reproducibility
numRuns?: number; // Number of test runs (default: 100)
maxSkipsPerRun?: number; // Max skipped tests before failing
timeout?: number; // Timeout per run (ms)
path?: string; // Path for replaying counterexample
unbiased?: boolean; // Disable bias towards edge cases
verbose?: boolean | VerbosityLevel; // Verbosity level
examples?: T[]; // Custom examples to test first
endOnFailure?: boolean; // Stop on first failure
reporter?: (runDetails: RunDetails<T>) => void;
asyncReporter?: (runDetails: RunDetails<T>) => Promise<void>;
}Compose and transform arbitraries to create custom generators.
function constant<T>(value: T): Arbitrary<T>;
function constantFrom<T>(...values: T[]): Arbitrary<T>;
function option<T, TNil = null>(arb: Arbitrary<T>, constraints?: OptionConstraints<TNil>): Arbitrary<T | TNil>;
function oneof<Ts extends MaybeWeightedArbitrary<unknown>[]>(...arbs: Ts | [OneOfConstraints, ...Ts]): Arbitrary<OneOfValue<Ts>>;
function letrec<T>(builder: LetrecTypedBuilder<T> | LetrecLooselyTypedBuilder<T>): LetrecValue<T>;
function memo<T>(builder: (maxDepth: number) => Arbitrary<T>): Memo<T>;Test stateful systems using command sequences.
function commands<Model, Real, CheckAsync>(
commandArbs: Arbitrary<AsyncCommand<Model, Real, CheckAsync>>[],
constraints?: CommandsContraints
): Arbitrary<Iterable<AsyncCommand<Model, Real, CheckAsync>>>;
function asyncModelRun<Model, Real, CheckAsync, InitialModel>(
s: ModelRunSetup<InitialModel, Real> | ModelRunAsyncSetup<InitialModel, Real>,
cmds: Iterable<AsyncCommand<Model, Real, CheckAsync>>
): Promise<void>;Detect race conditions by controlling async execution order.
function scheduler<TMetaData = unknown>(constraints?: SchedulerConstraints): Arbitrary<Scheduler<TMetaData>>;
function schedulerFor<TMetaData = unknown>(
customOrderingOrConstraints?: number[] | SchedulerConstraints,
constraintsOrUndefined?: SchedulerConstraints
): Scheduler<TMetaData> | ((strs: TemplateStringsArray, ...ordering: number[]) => Scheduler<TMetaData>);// Property types
interface IProperty<Ts> { /* property definition */ }
interface IPropertyWithHooks<Ts> extends IProperty<Ts> {
beforeEach(hookFunction: PropertyHookFunction): IPropertyWithHooks<Ts>;
afterEach(hookFunction: PropertyHookFunction): IPropertyWithHooks<Ts>;
}
interface IAsyncProperty<Ts> { /* async property definition */ }
interface IAsyncPropertyWithHooks<Ts> extends IAsyncProperty<Ts> {
beforeEach(hookFunction: AsyncPropertyHookFunction): IAsyncPropertyWithHooks<Ts>;
afterEach(hookFunction: AsyncPropertyHookFunction): IAsyncPropertyWithHooks<Ts>;
}
// Result types
type RunDetails<Ts> =
| RunDetailsSuccess<Ts>
| RunDetailsFailureProperty<Ts>
| RunDetailsFailureTooManySkips<Ts>
| RunDetailsFailureInterrupted<Ts>;
interface RunDetailsSuccess<Ts> {
failed: false;
numRuns: number;
numSkips: number;
numShrinks: number;
seed: number;
counterexamplePath: string | null;
error: null;
failures: [];
executionSummary: ExecutionTree<Ts>[];
verbose: VerbosityLevel;
}
interface RunDetailsFailureProperty<Ts> {
failed: true;
numRuns: number;
numSkips: number;
numShrinks: number;
seed: number;
counterexamplePath: string;
counterexample: Ts;
error: string | Error | unknown;
failures: Ts[];
executionSummary: ExecutionTree<Ts>[];
verbose: VerbosityLevel;
}
enum VerbosityLevel {
None = 0,
Verbose = 1,
VeryVerbose = 2
}
// Error class
class PreconditionFailure extends Error {
readonly interruptExecution: true;
readonly footprint: string;
}The abstract base class for all arbitraries.
abstract class Arbitrary<T> {
abstract generate(mrng: Random, biasFactor: number | undefined): Value<T>;
canShrinkWithoutContext(value: unknown): value is T;
shrink(value: T, context: unknown | undefined): Stream<Value<T>>;
filter<U extends T>(refinement: (t: T) => t is U): Arbitrary<U>;
filter(predicate: (t: T) => boolean): Arbitrary<T>;
map<U>(mapper: (t: T) => U, unmapper?: (u: unknown) => T): Arbitrary<U>;
chain<U>(chainer: (t: T) => Arbitrary<U>): Arbitrary<U>;
}