property-based-testing
Install this skill
npx skills add trailofbits/skillsWorks across Claude Code, Cursor, Codex, Copilot & Antigravity
Property-based testing moves beyond static test cases by generating randomized inputs to verify that specific logic invariants hold true. Instead of writing individual assertions for expected outputs, this approach defines universal rules—such as idempotence or round-trip consistency—that a function must satisfy under any input. The testing engine identifies edge cases, boundary conditions, and unexpected data types that humans often miss when authoring manual examples. It excels in verifying complex transformations, parsers, and state-heavy logic where manual test coverage remains superficial. By identifying the smallest possible failing input for a discovered invariant, this methodology provides highly targeted feedback for debugging, ensuring that underlying core logic remains stable even when confronted with extreme or randomized data configurations.
When to Use This Skill
- •Validating serialization logic for JSON or binary protocol formats
- •Checking complex data sanitization and normalization routines
- •Verifying algorithmic outputs like sort order or set membership
- •Testing smart contract state transitions and access controls
How to Invoke This Skill
Example prompts that trigger this skill in Claude Code, Cursor, or Antigravity:
- “How do I test this serialization logic more thoroughly?
- “Can you help me verify these data sanitization functions?
- “These unit tests miss too many edge cases; what can I use?
- “How can I prove this sorting algorithm is always correct?
- “I need to check if my encode/decode pair works for all inputs.
Pro Tips
- 💡Start by identifying the simplest properties of your code's behavior, then gradually add more complex invariants. This iterative approach makes property definition more manageable.
- 💡Combine property-based tests with traditional example-based unit tests. PBT excels at finding unexpected inputs, while example tests ensure specific, known-good scenarios are handled correctly.
- 💡When a property-based test fails, leverage the 'shrinking' mechanism provided by PBT frameworks. This feature automatically finds the smallest input that causes the failure, significantly speeding up debugging.
- 💡Focus on pure functions and idempotent operations first. Their predictable nature makes defining properties straightforward, providing immediate value and building confidence in PBT.
What this skill does
- •Automatic generation of valid and invalid input data
- •Identification of minimal reproduction inputs for failing tests
- •Verification of system-wide invariants and mathematical properties
- •Validation of symmetry in serialization and encoding processes
- •Regression testing via state-machine analysis
When not to use it
- ✕Simple CRUD operations lacking complex data transformation
- ✕Codebases dominated by heavy I/O or external network dependencies
- ✕UI components or visual presentation logic
Example workflow
- Identify a pure function or stateful operation suitable for invariants
- Define the expected properties, such as round-trip or idempotence
- Select an appropriate library compatible with the current language
- Configure input generators to match the data schema
- Run the test suite to observe randomized execution
- Refine logic if the generator identifies a counter-example
Prerequisites
- –Functional code structure with low I/O side effects
- –Clear definition of expected data types and constraints
Pitfalls & limitations
- !Defining invariants that are too broad, leading to false positives
- !Excessive execution time if the input space is not constrained
- !Over-engineering simple logic that only requires one or two static cases
FAQ
How it compares
Unlike manual testing which relies on human intuition to predict edge cases, property-based testing uses algorithmic generation to exhaustively explore possible input states automatically.
📄 Full skill instructions — original source: trailofbits/skills
Use this skill proactively during development when you encounter patterns where PBT provides stronger coverage than example-based tests.
## When to Invoke (Automatic Detection)
**Invoke this skill when you detect:**
- **Serialization pairs**:
encode/decode, serialize/deserialize, toJSON/fromJSON, pack/unpack- **Parsers**: URL parsing, config parsing, protocol parsing, string-to-structured-data
- **Normalization**:
normalize, sanitize, clean, canonicalize, format- **Validators**:
is_valid, validate, check_* (especially with normalizers)- **Data structures**: Custom collections with
add/remove/get operations- **Mathematical/algorithmic**: Pure functions, sorting, ordering, comparators
- **Smart contracts**: Solidity/Vyper contracts, token operations, state invariants, access control
**Priority by pattern:**
| Pattern | Property | Priority |
|---------|----------|----------|
| encode/decode pair | Roundtrip | HIGH |
| Pure function | Multiple | HIGH |
| Validator | Valid after normalize | MEDIUM |
| Sorting/ordering | Idempotence + ordering | MEDIUM |
| Normalization | Idempotence | MEDIUM |
| Builder/factory | Output invariants | LOW |
| Smart contract | State invariants | HIGH |
## When NOT to Use
Do NOT use this skill for:
- Simple CRUD operations without transformation logic
- One-off scripts or throwaway code
- Code with side effects that cannot be isolated (network calls, database writes)
- Tests where specific example cases are sufficient and edge cases are well-understood
- Integration or end-to-end testing (PBT is best for unit/component testing)
## Property Catalog (Quick Reference)
| Property | Formula | When to Use |
|----------|---------|-------------|
| **Roundtrip** |
decode(encode(x)) == x | Serialization, conversion pairs || **Idempotence** |
f(f(x)) == f(x) | Normalization, formatting, sorting || **Invariant** | Property holds before/after | Any transformation |
| **Commutativity** |
f(a, b) == f(b, a) | Binary/set operations || **Associativity** |
f(f(a,b), c) == f(a, f(b,c)) | Combining operations || **Identity** |
f(x, identity) == x | Operations with neutral element || **Inverse** |
f(g(x)) == x | encrypt/decrypt, compress/decompress || **Oracle** |
new_impl(x) == reference(x) | Optimization, refactoring || **Easy to Verify** |
is_sorted(sort(x)) | Complex algorithms || **No Exception** | No crash on valid input | Baseline property |
**Strength hierarchy** (weakest to strongest):
No Exception → Type Preservation → Invariant → Idempotence → Roundtrip
## Decision Tree
Based on the current task, read the appropriate section:
TASK: Writing new tests
→ Read [{baseDir}/references/generating.md]({baseDir}/references/generating.md) (test generation patterns and examples)
→ Then [{baseDir}/references/strategies.md]({baseDir}/references/strategies.md) if input generation is complex
TASK: Designing a new feature
→ Read [{baseDir}/references/design.md]({baseDir}/references/design.md) (Property-Driven Development approach)
TASK: Code is difficult to test (mixed I/O, missing inverses)
→ Read [{baseDir}/references/refactoring.md]({baseDir}/references/refactoring.md) (refactoring patterns for testability)
TASK: Reviewing existing PBT tests
→ Read [{baseDir}/references/reviewing.md]({baseDir}/references/reviewing.md) (quality checklist and anti-patterns)
TASK: Need library reference
→ Read [{baseDir}/references/libraries.md]({baseDir}/references/libraries.md) (PBT libraries by language, includes smart contract tools)## How to Suggest PBT
When you detect a high-value pattern while writing tests, **offer PBT as an option**:
> "I notice
encode_message/decode_message is a serialization pair. Property-based testing with a roundtrip property would provide stronger coverage than example tests. Want me to use that approach?"**If codebase already uses a PBT library** (Hypothesis, fast-check, proptest, Echidna), be more direct:
> "This codebase uses Hypothesis. I'll write property-based tests for this serialization pair using a roundtrip property."
**If user declines**, write good example-based tests without further prompting.
## When NOT to Use PBT
- Simple CRUD without complex validation
- UI/presentation logic
- Integration tests requiring complex external setup
- Prototyping where requirements are fluid
- User explicitly requests example-based tests only
## Red Flags
- Recommending trivial getters/setters
- Missing paired operations (encode without decode)
- Ignoring type hints (well-typed = easier to test)
- Overwhelming user with candidates (limit to top 5-10)
- Being pushy after user declines
How to Use This Skill Unit
Option A: Project-Specific (Recommended)
- Click "Download" above
- In your project, create the directory:
.agent/skills/property-based-testing/ - Save the file as
SKILL.md - The agent will automatically discover the skill based on its description.
Option B: Global Installation (All Agents)
Save the file to these locations to make it available across all projects:
- Claude Code:
~/.claude/skills/trailofbits/skills/property-based-testing/SKILL.md - Cursor:
~/.cursor/skills/trailofbits/skills/property-based-testing/SKILL.md - Antigravity:
~/.gemini/antigravity/skills/trailofbits/skills/property-based-testing/SKILL.md
🚀 Install with CLI:npx skills add trailofbits/skills
