Property based testing framework for JavaScript (like QuickCheck)
Combinators compose and transform arbitraries to create custom generators. They enable building complex arbitraries from simpler ones while preserving shrinking behavior.
Generate constant or randomly selected constant values.
/**
* Always generate the same constant value
* @param value - The constant value to generate
* @returns Arbitrary always generating the same value
*/
function constant<T>(value: T): Arbitrary<T>;
/**
* Generate one value randomly selected from provided constants
* @param values - List of constant values to choose from
* @returns Arbitrary generating one of the constants
*/
function constantFrom<T>(...values: T[]): Arbitrary<T>;
/**
* Generate values by mapping integers to constants using weighted groups
* @param entries - Weighted groups of value builders
* @returns Arbitrary generating values from weighted groups
*/
function mapToConstant<T>(...entries: { num: number; build: (idInGroup: number) => T }[]): Arbitrary<T>;Usage Examples:
import { constant, constantFrom, mapToConstant, property, assert } from 'fast-check';
// Always generate the same value
assert(
property(constant(42), (n) => {
return n === 42;
})
);
// Choose from a set of values
assert(
property(constantFrom('red', 'green', 'blue'), (color) => {
return ['red', 'green', 'blue'].includes(color);
})
);
// Weighted selection
const weightedColors = mapToConstant(
{ num: 5, build: () => 'red' },
{ num: 3, build: () => 'green' },
{ num: 2, build: () => 'blue' }
);Generate optional values (value or nil).
/**
* Generate either nil (default: null) or a value from source arbitrary
* @param arb - Source arbitrary
* @param constraints - Nil value and frequency constraints
* @returns Arbitrary generating value or nil
*/
function option<T, TNil = null>(
arb: Arbitrary<T>,
constraints?: OptionConstraints<TNil>
): Arbitrary<T | TNil>;
interface OptionConstraints<TNil> {
nil?: TNil;
freq?: number;
depthIdentifier?: DepthIdentifier | string;
}Usage Example:
import { option, string, property, assert } from 'fast-check';
// Optional string (string or null)
assert(
property(option(string()), (maybeStr) => {
return maybeStr === null || typeof maybeStr === 'string';
})
);
// Optional with custom nil
assert(
property(option(string(), { nil: undefined }), (maybeStr) => {
return maybeStr === undefined || typeof maybeStr === 'string';
})
);Generate values from one of several arbitraries.
/**
* Generate values from one of several arbitraries
* @param arbs - List of arbitraries or constraints with arbitraries
* @returns Arbitrary generating values from one of the sources
*/
function oneof<Ts extends MaybeWeightedArbitrary<unknown>[]>(
...arbs: Ts | [OneOfConstraints, ...Ts]
): Arbitrary<OneOfValue<Ts>>;
interface OneOfConstraints {
maxDepth?: number;
depthSize?: DepthSize;
depthIdentifier?: DepthIdentifier | string;
withCrossShrink?: boolean;
}
type MaybeWeightedArbitrary<T> = Arbitrary<T> | WeightedArbitrary<T>;
interface WeightedArbitrary<T> {
arbitrary: Arbitrary<T>;
weight: number;
}
type OneOfValue<Ts extends readonly unknown[]> =
Ts[number] extends Arbitrary<infer T> ? T :
Ts[number] extends WeightedArbitrary<infer T> ? T :
never;Usage Examples:
import { oneof, string, integer, boolean, property, assert } from 'fast-check';
// Choose from multiple arbitraries
assert(
property(oneof(string(), integer(), boolean()), (value) => {
return (
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'boolean'
);
})
);
// Weighted selection
assert(
property(
oneof(
{ arbitrary: string(), weight: 5 },
{ arbitrary: integer(), weight: 1 }
),
(value) => {
return typeof value === 'string' || typeof value === 'number';
}
)
);Generate N identical copies of a value.
/**
* Clone values generated by an arbitrary to produce N identical copies
* @param arb - Source arbitrary
* @param numValues - Number of clones to produce
* @returns Arbitrary generating tuple of N identical values
*/
function clone<T, N extends number>(arb: Arbitrary<T>, numValues: N): Arbitrary<CloneValue<T, N>>;
type CloneValue<T, N extends number> = { [K in number]: T } & { length: N };Usage Example:
import { clone, string, property, assert } from 'fast-check';
// Generate 3 identical strings
assert(
property(clone(string(), 3), ([a, b, c]) => {
return a === b && b === c;
})
);Modify arbitrary behavior for shrinking and bias.
/**
* Disable shrinking capabilities of an arbitrary
* @param arb - Source arbitrary
* @returns Arbitrary without shrinking
*/
function noShrink<T>(arb: Arbitrary<T>): Arbitrary<T>;
/**
* Disable bias in value generation (forces uniform distribution)
* @param arb - Source arbitrary
* @returns Arbitrary without bias
*/
function noBias<T>(arb: Arbitrary<T>): Arbitrary<T>;
/**
* Limit the number of shrink attempts for an arbitrary
* @param arbitrary - Source arbitrary
* @param maxShrinks - Maximum number of shrink operations
* @returns Arbitrary with limited shrinking
*/
function limitShrink<T>(arbitrary: Arbitrary<T>, maxShrinks: number): Arbitrary<T>;Usage Examples:
import { noShrink, noBias, limitShrink, integer, property, assert } from 'fast-check';
// Disable shrinking (useful for debugging or performance)
const noShrinkInt = noShrink(integer());
// Force uniform distribution
const uniformInt = noBias(integer({ min: 0, max: 100 }));
// Limit shrink attempts (useful for complex generators)
const limitedInt = limitShrink(integer(), 100);Create recursive and mutually recursive arbitraries.
/**
* Create mutually recursive arbitraries using tie function
* @param builder - Function that builds recursive structure
* @returns Object containing all defined arbitraries
*/
function letrec<T>(
builder: LetrecTypedBuilder<T> | LetrecLooselyTypedBuilder<T>
): LetrecValue<T>;
/**
* Create memoized recursive arbitraries with depth control
* @param builder - Function receiving maxDepth and returning arbitrary
* @returns Memoized arbitrary function
*/
function memo<T>(builder: (maxDepth: number) => Arbitrary<T>): Memo<T>;
type LetrecValue<T> = { [K in keyof T]: Arbitrary<T[K]> };
type LetrecTypedBuilder<T> = (tie: LetrecTypedTie<T>) => LetrecValue<T>;
type LetrecTypedTie<T> = <K extends keyof T>(key: K) => Arbitrary<T[K]>;
type Memo<T> = (maxDepth?: number) => Arbitrary<T>;Usage Examples:
import { letrec, memo, record, array, string, integer, property, assert, option } from 'fast-check';
// Binary tree using letrec
type Tree = { value: number; left: Tree | null; right: Tree | null };
const treeArb = letrec((tie) => ({
tree: record({
value: integer(),
left: option(tie('tree')),
right: option(tie('tree')),
}),
})).tree;
// Nested array using memo
const nestedArrayArb = memo((maxDepth) => {
if (maxDepth <= 0) {
return integer();
}
return oneof(integer(), array(nestedArrayArb(maxDepth - 1)));
});
assert(
property(nestedArrayArb(3), (value) => {
return value !== undefined;
})
);Generate interconnected entities with relationships.
/**
* Generate interconnected entities with relationships
* @param arbitraries - Arbitraries for entity fields
* @param relations - Relationship definitions between entities
* @param constraints - Size and depth constraints
* @returns Arbitrary generating entity graphs
*/
function entityGraph<TEntityFields, TEntityRelations>(
arbitraries: EntityGraphArbitraries<TEntityFields>,
relations: TEntityRelations,
constraints?: EntityGraphContraints<TEntityFields>
): Arbitrary<EntityGraphValue<TEntityFields, TEntityRelations>>;
type EntityGraphArbitraries<TEntityFields> = {
[K in keyof TEntityFields]: Arbitrary<TEntityFields[K]>;
};
type EntityGraphRelations<TEntityFields> = {
[relationName: string]: [keyof TEntityFields, keyof TEntityFields, number, number];
};
type EntityGraphContraints<TEntityFields> = {
maxDepth?: number;
depthIdentifier?: DepthIdentifier | string;
[K in keyof TEntityFields]?: { num: number };
};
type EntityGraphValue<TEntityFields, TEntityRelations> = {
[K in keyof TEntityFields]: Array<TEntityFields[K] & { [rel: string]: any }>;
};Usage Example:
import { entityGraph, string, integer, record, property, assert } from 'fast-check';
// User and Post entities with relationships
const socialGraph = entityGraph(
{
user: record({ name: string(), age: integer({ min: 13, max: 120 }) }),
post: record({ title: string(), content: string() }),
},
{
author: ['post', 'user', 1, 1], // Each post has exactly 1 author
posts: ['user', 'post', 0, 10], // Each user has 0-10 posts
}
);
assert(
property(socialGraph, (graph) => {
return graph.user.length > 0 || graph.post.length > 0;
})
);Generate pure functions with deterministic behavior.
/**
* Generate pure functions that deterministically map inputs to outputs
* @param arb - Arbitrary for function output values
* @returns Arbitrary generating functions
*/
function func<TArgs extends any[], TOut>(arb: Arbitrary<TOut>): Arbitrary<(...args: TArgs) => TOut>;
/**
* Generate comparison functions suitable for Array.sort
* @returns Arbitrary generating comparison functions
*/
function compareFunc<T>(): Arbitrary<(a: T, b: T) => number>;
/**
* Generate boolean comparison functions
* @returns Arbitrary generating boolean comparison functions
*/
function compareBooleanFunc<T>(): Arbitrary<(a: T, b: T) => boolean>;Usage Examples:
import { func, compareFunc, integer, string, array, property, assert } from 'fast-check';
// Generate functions
assert(
property(func<[number, number], string>(string()), (fn) => {
const result1 = fn(1, 2);
const result2 = fn(1, 2);
return result1 === result2; // Deterministic
})
);
// Generate comparison functions
assert(
property(array(integer()), compareFunc<number>(), (arr, cmp) => {
const sorted = [...arr].sort(cmp);
return sorted.length === arr.length;
})
);type DepthSize = 'small' | 'medium' | 'large';
type DepthIdentifier = { readonly name: string };Install with Tessl CLI
npx tessl i tessl/npm-fast-check