jsproptest¶
jsproptest is a property-based testing (PBT) framework for JavaScript and TypeScript, drawing inspiration from libraries such as Haskell's QuickCheck and Python's Hypothesis. Property-based testing shifts the focus from example-based verification to defining universal properties or invariants that must hold true for an input domain.
Instead of manually crafting test cases for specific inputs, PBT allows you to describe the domain of inputs your function expects and the general characteristics of the output (e.g., add(a, b) should always be greater than or equal to a and b if they are non-negative). PBT then generates hundreds or thousands of varied inputs, searching for edge cases or unexpected behaviors that violate your defined properties. This approach significantly increases test coverage and the likelihood of finding subtle bugs.
The core workflow involves:
- Defining a property: A function that takes generated inputs and asserts an expected invariant. See Properties.
- Specifying generators: Mechanisms for creating random data conforming to certain types or constraints, often built by composing simpler generators using combinators. See Generators and Combinators.
- Execution:
jsproptestautomatically runs the property function against numerous generated inputs (typically 100+). - Shrinking: If a test case fails (the property returns
falseor throws),jsproptestattempts to find a minimal counterexample by simplifying the failing input. See Shrinking.
Consider verifying a round-trip property for a custom parser/serializer:
import { forAll, Gen } from 'jsproptest';
import { deepStrictEqual } from 'assert'; // Use Node.js assert for deep equality
it('should preserve data after serializing and parsing', () => {
// Generator for keys (non-empty strings without '&' or '=')
const keyGen = Gen.string(1, 10).filter(s => s.length > 0 && !s.includes('&') && !s.includes('='));
// Generator for arbitrary string values
const valueGen = Gen.string(0, 10);
// Generator for objects (dictionaries) with our specific keys and values
const dataObjectGen = Gen.dict(keyGen, valueGen, 0, 10);
// forAll executes the property check with generated data objects
forAll(
(originalData: Record<string, string>) => {
// Perform the round trip: serialize then parse
const serialized = serializeMyDataFormat(originalData);
const parsedData = parseMyDataFormat(serialized);
// Property: The parsed data must deep-equal the original data object.
// Using deepStrictEqual ensures type and value equality.
deepStrictEqual(parsedData, originalData);
},
dataObjectGen // Use the dictionary generator
);
// jsproptest runs this property multiple times with diverse data objects.
});
This PBT approach facilitates the discovery of edge cases and intricate bugs that might be neglected by traditional, example-driven testing methodologies.
Getting Started¶
Installation¶
To add jsproptest to your project, run the following command:
npm install jsproptest --save-dev
devDependencies.
Core Concepts and Features¶
Understanding these key components will help you use jsproptest effectively:
-
Generators: Produce random data of various types (primitives, containers) according to specified constraints (e.g.,
Gen.integer(),Gen.array(...)). Learn how to create the basic building blocks of your test data. -
Combinators: Modify or combine existing generators to create new ones. Discover techniques to constraint, combine, and transform generators for complex data structures.
-
Properties (
Property,forAll): Express conditions or invariants that should hold true for generated data.jsproptestruns these properties against many generated examples using theforAllfunction orPropertyclass methods. Understand how to define the invariants your code should satisfy and how to run tests. -
Shrinking: When a property fails,
jsproptestattempts to find a minimal counterexample by simplifying the failing input using logic associated with the generated value (often via aShrinkablestructure). See howjsproptesthelps pinpoint failures. -
Stateful Testing: Go beyond simple input-output functions and test systems with internal state by generating sequences of operations or commands. Learn how to model and verify stateful behaviors.