Add Property Rapid
Streamline your workflow with this implement, property, based, testing. Includes structured workflows, validation checks, and reusable patterns for testing.
Add Property Rapid
Add Property Rapid is a command that quickly scaffolds a property-based testing framework into your project, identifying testable properties, generating custom data generators, and integrating with your existing test suite. Property-based testing differs from traditional example-based testing by defining general properties that should hold for all valid inputs, then automatically generating hundreds or thousands of random inputs to find violations. This command accelerates adoption by handling the boilerplate and helping you discover which functions in your codebase are strong candidates for property tests.
When to Use This Command
Run this command when...
- Your codebase contains functions with mathematical properties such as commutativity, associativity, idempotency, or round-trip encoding/decoding that should hold for all inputs.
- You have found bugs in production that example-based tests missed because the failing input was an edge case nobody anticipated.
- You are building serialization, parsing, or data transformation logic where encode/decode round-trip correctness is critical.
- Your business rules define invariants (e.g., "account balance must never go negative") that should be validated across a wide range of scenarios.
- You want to improve test coverage rapidly without manually crafting dozens of individual test cases for each function.
Consider alternatives when...
- The code under test involves heavy side effects like network calls or database writes that are expensive to mock for thousands of generated inputs; targeted integration tests may be more practical.
- You need to verify specific known edge cases rather than exploring the input space broadly; traditional unit tests are more direct for this purpose.
- The team has no existing test infrastructure and needs to set up basic unit testing before layering on property-based approaches.
Quick Start
# property-testing.config.yml framework: fast-check # fast-check | hypothesis | quickcheck | proptest integration: jest # existing test runner properties: round_trip: true # auto-detect encode/decode pairs invariants: true # auto-detect business invariants idempotency: true # auto-detect idempotent operations generation: num_runs: 200 # inputs generated per property seed: 42 # reproducible randomness
Example invocation:
add-property-rapid "typescript fast-check with jest"
Example output:
Property-Based Testing Setup Complete
--------------------------------------
Framework: fast-check v3.20
Integration: Jest (existing configuration detected)
Language: TypeScript
Properties Identified:
1. formatCurrency / parseCurrency --> round-trip property
2. sortItems (stable sort) --> idempotency property
3. calculateDiscount --> invariant: result <= original
4. encodeBase64 / decodeBase64 --> round-trip property
Files Created:
src/__tests__/properties/
currency.property.test.ts - 3 properties, custom Money generator
sorting.property.test.ts - 2 properties
discount.property.test.ts - 2 properties with preconditions
encoding.property.test.ts - 1 round-trip property
Custom Generators Created:
MoneyArbitrary - valid currency amounts (0.01 - 999999.99)
ItemListArbitrary - arrays of items with realistic field values
DiscountArbitrary - valid discount configurations (0-100%)
Run: npx jest --testPathPattern=property
Core Concepts
| Concept | Purpose | Details |
|---|---|---|
| Property | A universally quantified assertion about code behavior | A statement like "for all valid inputs x, encode(decode(x)) equals x" that must hold across every generated test case |
| Arbitrary (Generator) | Produces random inputs conforming to constraints | A composable data generator that creates valid test inputs, respecting domain constraints such as ranges, formats, and structural rules |
| Shrinking | Minimizes failing inputs for easier debugging | When a property violation is found, the framework automatically reduces the failing input to the smallest case that still triggers the failure |
| Precondition | Filters generated inputs to valid subsets | A guard clause like assume(age >= 0) that discards generated values which do not satisfy domain requirements, preventing false failures |
| Seed | Enables reproducible test runs | A fixed random seed that guarantees the same sequence of generated inputs, allowing failures to be reproduced exactly during debugging |
Add Property Rapid Architecture
+------------------------------------------------------+
| CODEBASE ANALYSIS |
| Scan Functions --> Identify Properties |
| [round-trip] [invariant] [idempotent] [commutative] |
+------------------------------------------------------+
|
v
+------------------------------------------------------+
| GENERATOR DESIGN |
| Analyze Types --> Build Arbitraries --> Compose |
| [primitives] [records] [arrays] [custom domains] |
+------------------------------------------------------+
|
v
+------------------------------------------------------+
| PROPERTY TEST GENERATION |
| Template Properties --> Add Preconditions |
| --> Wire Generators --> Integrate with Test Runner |
+------------------------------------------------------+
|
v
+------------------------------------------------------+
| EXECUTION & SHRINKING |
| Run N Inputs --> On Failure: Shrink --> Report Min |
+------------------------------------------------------+
Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
framework | string | auto-detect | Property testing library to use: fast-check, hypothesis, quickcheck, proptest, or jqwik |
num_runs | integer | 100 | Number of randomly generated inputs to test each property against per execution |
seed | integer | random | Fixed seed for reproducible test generation; omit for true randomness on each run |
shrink_depth | integer | 100 | Maximum number of shrinking iterations to attempt when minimizing a failing input |
detect_properties | boolean | true | Whether to automatically scan the codebase for functions that are candidates for property tests |
Best Practices
-
Begin with round-trip properties because they are easy to identify and high-value. Any pair of functions where one reverses the other (serialize/deserialize, encode/decode, format/parse) is a natural candidate. The property
decode(encode(x)) === xis universally understood by teams and catches a wide class of bugs including encoding errors, precision loss, and character handling issues. This builds team confidence in the approach. -
Design generators that reflect real-world data distributions. A generator producing uniformly random strings will never create realistic email addresses, currency values, or timestamps. Invest time in building domain-specific generators that produce inputs matching production patterns. This makes property tests more likely to find bugs that actually occur rather than exercising unrealistic edge cases that never appear in practice.
-
Keep property definitions declarative and concise. A property test should read like a specification: "for all valid orders, the total equals the sum of line items." If a property test requires complex setup or assertion logic, it becomes hard to maintain and reason about. Extract complexity into helper functions and generators so the property itself remains a clear statement of intended behavior.
-
Use preconditions sparingly and document why they exist. Every precondition narrows the input space, potentially hiding bugs in the excluded region. When you add an
assume()clause, add a comment explaining why those inputs are genuinely invalid rather than just inconvenient. Periodically review preconditions to ensure they still reflect actual business constraints. -
Save and version-control failing seeds. When a property test discovers a failure, record the seed value in your test configuration or a dedicated seeds file. This creates a regression test library that ensures previously discovered bugs remain covered even as generators evolve. Most frameworks support replaying specific seeds, making this a zero-cost way to accumulate high-value test cases.
Common Issues
Generator produces too many discarded inputs, causing slow test runs. This happens when preconditions reject a large percentage of generated values. Rather than filtering with assume(), restructure the generator to produce only valid inputs from the start. For example, instead of generating any integer and assuming it is positive, use a generator that directly produces positive integers.
Shrinking produces a minimal example that is hard to interpret. Sometimes the smallest failing input is technically minimal but semantically confusing (e.g., an empty string or zero). Add custom shrinkers that preserve domain meaning during minimization. For example, shrink a failing date toward a known "simple" date like January 1st rather than toward epoch zero, which may be meaningless in your domain.
Property tests pass locally but fail in CI with different seeds. Unless you pin a seed, each run uses different random inputs. A flaky property test indicates either a genuine intermittent bug or a property that is not universally true. Capture the failing seed from the CI logs, reproduce locally, and fix the underlying issue. Then add that seed to your pinned seeds for permanent regression coverage.
Reviews
No reviews yet. Be the first to review this template!
Similar Templates
Git Commit Message Generator
Generates well-structured conventional commit messages by analyzing staged changes. Follows Conventional Commits spec with scope detection.
React Component Scaffolder
Scaffolds a complete React component with TypeScript types, Tailwind styles, Storybook stories, and unit tests. Follows project conventions automatically.
CI/CD Pipeline Generator
Generates GitHub Actions workflows for CI/CD including linting, testing, building, and deploying. Detects project stack automatically.