Get the FREE Ultimate OpenClaw Setup Guide →

property-based-testing

Scanned
npx machina-cli add skill ed3dai/ed3d-plugins/property-based-testing --openclaw
Files (1)
SKILL.md
5.2 KB

Property-Based Testing

Overview

Property-based testing (PBT) generates random inputs and verifies that properties hold for all of them. Instead of testing specific examples, you test invariants.

When PBT beats example-based tests:

  • Serialization pairs (encode/decode)
  • Pure functions with clear contracts
  • Validators and normalizers
  • Data structure operations

Property Catalog

PropertyFormulaWhen to Use
Roundtripdecode(encode(x)) == xSerialization, conversion pairs
Idempotencef(f(x)) == f(x)Normalization, formatting, sorting
InvariantProperty holds before/afterAny transformation
Commutativityf(a, b) == f(b, a)Binary/set operations
Associativityf(f(a,b), c) == f(a, f(b,c))Combining operations
Identityf(x, identity) == xOperations with neutral element
Inversef(g(x)) == xencrypt/decrypt, compress/decompress
Oraclenew_impl(x) == reference(x)Optimization, refactoring
Easy to Verifyis_sorted(sort(x))Complex algorithms
No ExceptionNo crash on valid inputBaseline (weakest)

Strength hierarchy (weakest to strongest):

No Exception -> Type Preservation -> Invariant -> Idempotence -> Roundtrip

Always aim for the strongest property that applies.

Pattern Detection

Use PBT when you see:

PatternPropertyPriority
encode/decode, serialize/deserializeRoundtripHIGH
toJSON/fromJSON, pack/unpackRoundtripHIGH
Pure functions with clear contractsMultipleHIGH
normalize, sanitize, canonicalizeIdempotenceMEDIUM
is_valid, validate with normalizersValid after normalizeMEDIUM
Sorting, ordering, comparatorsIdempotence + orderingMEDIUM
Custom collections (add/remove/get)InvariantsMEDIUM
Builder/factory patternsOutput invariantsLOW

When NOT to Use

  • Simple CRUD without transformation logic
  • UI/presentation logic
  • Integration tests requiring complex external setup
  • Code with side effects that cannot be isolated
  • Prototyping where requirements are fluid
  • Tests where specific examples suffice and edge cases are understood

Library Quick Reference

LanguageLibraryImport
PythonHypothesisfrom hypothesis import given, strategies as st
TypeScript/JSfast-checkimport fc from 'fast-check'
Rustproptestuse proptest::prelude::*
Gorapidimport "pgregory.net/rapid"
Javajqwik@Property annotations
HaskellQuickCheckimport Test.QuickCheck

For library-specific syntax and patterns: Use @ed3d-research-agents:internet-researcher to get current documentation.

Input Strategy Best Practices

  1. Constrain early: Build constraints INTO the strategy, not via assume()

    # GOOD
    st.integers(min_value=1, max_value=100)
    
    # BAD - high rejection rate
    st.integers().filter(lambda x: 1 <= x <= 100)
    
  2. Size limits: Prevent slow tests

    st.lists(st.integers(), max_size=100)
    st.text(max_size=1000)
    
  3. Realistic data: Match real-world constraints

    st.integers(min_value=0, max_value=150)  # Real ages, not arbitrary ints
    
  4. Reuse strategies: Define once, use across tests

    valid_users = st.builds(User, ...)
    
    @given(valid_users)
    def test_one(user): ...
    
    @given(valid_users)
    def test_two(user): ...
    

Settings Guide

# Development (fast feedback)
@settings(max_examples=10)

# CI (thorough)
@settings(max_examples=200)

# Nightly/Release (exhaustive)
@settings(max_examples=1000, deadline=None)

Quality Checklist

Before committing PBT tests:

  • Not tautological (assertion doesn't compare same expression)
  • Strong assertion (not just "no crash")
  • Not vacuous (inputs not over-filtered by assume())
  • Edge cases covered with explicit examples (@example)
  • No reimplementation of function logic in assertion
  • Strategy constraints are realistic
  • Settings appropriate for context

Red Flags

  • Tautological: assert sorted(xs) == sorted(xs) tests nothing
  • Only "no crash": Always look for stronger properties
  • Vacuous: Multiple assume() calls filter out most inputs
  • Reimplementation: assert add(a, b) == a + b if that's how add is implemented
  • Missing edge cases: No @example([]), @example([1]) decorators
  • Overly constrained: Many assume() calls means redesign the strategy

Common Mistakes

MistakeFix
Testing mock behaviorTest real behavior
Reimplementing function in testUse algebraic properties
Filtering with assume()Build constraints into strategy
No edge case examplesAdd @example decorators
One property onlyAdd multiple properties (length, ordering, etc.)

Source

git clone https://github.com/ed3dai/ed3d-plugins/blob/main/plugins/ed3d-house-style/skills/property-based-testing/SKILL.mdView on GitHub

Overview

Property-based testing (PBT) generates random inputs and checks that properties hold for all of them. It is especially powerful for serialization, pure functions, validators, and normalizers, and it provides a property catalog and pattern-detection guidance to shape tests.

How This Skill Works

PBT defines properties as invariants and uses a test runner to generate many inputs that exercise them. When a counterexample is found, the engine reports it and typically shrinks it to the smallest failing case. The approach leverages a Property Catalog (Roundtrip, Idempotence, Invariant, etc.) and pattern detection to pick effective properties and libraries.

When to Use It

  • Serialization roundtrips (encode/decode) and similar convert/parse paths
  • Pure functions with clear contracts or mathematical invariants
  • Validators and normalizers where postconditions matter
  • Data structure operations and transformations with invariants
  • Refactoring or optimization where a reference implementation exists (Oracle)

Quick Start

  1. Step 1: Pick a property to test (eg, Roundtrip for serialization)
  2. Step 2: Write a property-based test using a library (eg Hypothesis, fast-check)
  3. Step 3: Run tests and let the engine shrink counterexamples to minimal failing cases

Best Practices

  • Aim for the strongest applicable property from the catalog
  • Constrain inputs directly in the strategy rather than filtering with assumptions
  • Keep test inputs realistic to mirror real-world constraints
  • Reuse and share strategies across tests to reduce duplication
  • Monitor test size to prevent slow or flaky tests

Example Use Cases

  • Roundtrip: test encode/decode consistency across data types
  • Idempotence: normalization or formatting functions stabilize after one pass
  • Invariant: transformations preserve essential properties across inputs
  • Commutativity/Associativity: test order independence in operations
  • Inverse: encrypt/decrypt or compress/decompress yield original data

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers