property-based-test-generator

Property-Based Test Generator

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "property-based-test-generator" with this command: npx skills add dexploarer/claudius-skills/dexploarer-claudius-skills-property-based-test-generator

Property-Based Test Generator

Generates property-based tests that validate invariants and find edge cases automatically through randomized testing.

When to Use

  • "Generate property-based tests"

  • "Create hypothesis tests for my function"

  • "Add property tests to my code"

  • "Generate fast-check tests"

  • "Test function properties"

  • "Find edge cases automatically"

Instructions

  1. Detect Language and Testing Framework

Check the project's language and existing test setup:

Check for Python

[ -f "pytest.ini" ] || [ -f "setup.py" ] && echo "Python"

Check for JavaScript/TypeScript

[ -f "package.json" ] && echo "JavaScript/TypeScript"

Check existing test framework

grep -E "(jest|vitest|mocha|pytest|hypothesis)" package.json pyproject.toml requirements.txt 2>/dev/null

  1. Install Property-Based Testing Library

Python (Hypothesis):

pip install hypothesis pytest

JavaScript/TypeScript (fast-check):

npm install --save-dev fast-check @types/jest

or

npm install --save-dev fast-check vitest

Haskell (QuickCheck):

cabal install QuickCheck

  1. Identify Function Properties

Analyze the function to test and identify invariants:

Common Properties:

  • Idempotence: f(f(x)) === f(x)

  • Inverse: decode(encode(x)) === x

  • Commutativity: f(a, b) === f(b, a)

  • Associativity: f(f(a, b), c) === f(a, f(b, c))

  • Identity: f(x, identity) === x

  • Range: Output always within valid range

  • Type safety: Output type matches expected

  • No exceptions: Function never throws for valid input

  1. Generate Property-Based Tests

Python with Hypothesis

Basic Example:

from hypothesis import given, strategies as st import pytest

Function to test

def sort_list(items): return sorted(items)

Property: sorted list length equals original

@given(st.lists(st.integers())) def test_sort_preserves_length(items): result = sort_list(items) assert len(result) == len(items)

Property: sorted list is ordered

@given(st.lists(st.integers())) def test_sort_creates_ordered_list(items): result = sort_list(items) for i in range(len(result) - 1): assert result[i] <= result[i + 1]

Property: sorted list contains same elements

@given(st.lists(st.integers())) def test_sort_preserves_elements(items): result = sort_list(items) assert sorted(items) == result

Advanced Strategies:

from hypothesis import given, strategies as st, assume from datetime import datetime, timedelta

Custom data structures

@st.composite def users(draw): return { 'id': draw(st.integers(min_value=1, max_value=1000000)), 'name': draw(st.text(min_size=1, max_size=50)), 'email': draw(st.emails()), 'age': draw(st.integers(min_value=18, max_value=120)), 'created_at': draw(st.datetimes( min_value=datetime(2020, 1, 1), max_value=datetime.now() )) }

@given(users()) def test_user_validation(user): # Validate user properties assert user['age'] >= 18 assert '@' in user['email'] assert len(user['name']) > 0 assert user['created_at'] <= datetime.now()

Testing with Preconditions:

@given(st.integers(), st.integers()) def test_division(a, b): assume(b != 0) # Precondition: no division by zero result = a / b assert result * b == a # Property: inverse of multiplication

Stateful Testing:

from hypothesis.stateful import RuleBasedStateMachine, rule, invariant

class ShoppingCart(RuleBasedStateMachine): def init(self): super().init() self.items = [] self.total = 0

@rule(item=st.tuples(st.text(), st.floats(min_value=0, max_value=1000)))
def add_item(self, item):
    name, price = item
    self.items.append(item)
    self.total += price

@rule()
def clear_cart(self):
    self.items = []
    self.total = 0

@invariant()
def total_matches_sum(self):
    assert abs(self.total - sum(p for _, p in self.items)) &#x3C; 0.01

TestCart = ShoppingCart.TestCase

JavaScript/TypeScript with fast-check

Basic Example:

import fc from 'fast-check';

// Function to test function reverseString(str: string): string { return str.split('').reverse().join(''); }

describe('reverseString', () => { it('double reverse returns original', () => { fc.assert( fc.property(fc.string(), (str) => { const reversed = reverseString(str); const doubleReversed = reverseString(reversed); return doubleReversed === str; }) ); });

it('preserves string length', () => { fc.assert( fc.property(fc.string(), (str) => { return reverseString(str).length === str.length; }) ); });

it('first char becomes last char', () => { fc.assert( fc.property(fc.string({ minLength: 1 }), (str) => { const reversed = reverseString(str); return str[0] === reversed[reversed.length - 1]; }) ); }); });

Complex Data Structures:

import fc from 'fast-check';

// Custom arbitraries const userArbitrary = fc.record({ id: fc.integer({ min: 1, max: 1000000 }), name: fc.string({ minLength: 1, maxLength: 50 }), email: fc.emailAddress(), age: fc.integer({ min: 18, max: 120 }), roles: fc.array(fc.constantFrom('admin', 'user', 'guest'), { minLength: 1 }) });

describe('User validation', () => { it('validates user structure', () => { fc.assert( fc.property(userArbitrary, (user) => { return ( user.age >= 18 && user.email.includes('@') && user.roles.length > 0 ); }) ); }); });

Array Properties:

describe('Array operations', () => { it('map preserves length', () => { fc.assert( fc.property( fc.array(fc.integer()), fc.func(fc.integer()), (arr, fn) => { return arr.map(fn).length === arr.length; } ) ); });

it('filter result is subset', () => { fc.assert( fc.property( fc.array(fc.integer()), (arr) => { const filtered = arr.filter(x => x > 0); return filtered.every(x => arr.includes(x)); } ) ); });

it('concat is associative', () => { fc.assert( fc.property( fc.array(fc.integer()), fc.array(fc.integer()), fc.array(fc.integer()), (a, b, c) => { const left = a.concat(b).concat(c); const right = a.concat(b.concat(c)); return JSON.stringify(left) === JSON.stringify(right); } ) ); }); });

Shrinking Examples:

describe('Shrinking demonstration', () => { it('finds minimal failing case', () => { fc.assert( fc.property(fc.array(fc.integer()), (arr) => { // This will fail and shrink to smallest failing case return arr.length < 5 || arr.some(x => x > 100); }), { numRuns: 100 } ); }); });

Model-Based Testing:

import fc from 'fast-check';

class Stack<T> { private items: T[] = [];

push(item: T): void { this.items.push(item); }

pop(): T | undefined { return this.items.pop(); }

size(): number { return this.items.length; } }

describe('Stack', () => { it('behaves like array', () => { fc.assert( fc.property( fc.array(fc.integer()), (operations) => { const stack = new Stack<number>(); const model: number[] = [];

      for (const op of operations) {
        if (op >= 0) {
          stack.push(op);
          model.push(op);
        } else {
          const stackResult = stack.pop();
          const modelResult = model.pop();
          if (stackResult !== modelResult) return false;
        }
      }

      return stack.size() === model.length;
    }
  )
);

}); });

  1. Common Property Patterns

Encode/Decode (Roundtrip):

from hypothesis import given, strategies as st import json

@given(st.dictionaries(st.text(), st.integers())) def test_json_roundtrip(data): encoded = json.dumps(data) decoded = json.loads(encoded) assert decoded == data

fc.assert( fc.property(fc.anything(), (data) => { const encoded = JSON.stringify(data); const decoded = JSON.parse(encoded); return JSON.stringify(decoded) === encoded; }) );

Idempotence:

@given(st.lists(st.integers())) def test_dedup_idempotent(items): result1 = list(set(items)) result2 = list(set(result1)) assert result1 == result2

Commutativity:

@given(st.integers(), st.integers()) def test_addition_commutative(a, b): assert a + b == b + a

Oracle (Compare with Known Implementation):

@given(st.lists(st.integers())) def test_custom_sort_matches_builtin(items): assert custom_sort(items) == sorted(items)

Invariants:

fc.assert( fc.property(fc.array(fc.integer()), (arr) => { const unique = [...new Set(arr)]; return unique.length <= arr.length; }) );

  1. Configuration Options

Hypothesis:

from hypothesis import given, settings, strategies as st

@settings( max_examples=1000, # Number of test cases deadline=None, # No timeout verbosity=hypothesis.Verbosity.verbose ) @given(st.integers()) def test_with_settings(x): assert x == x

fast-check:

fc.assert( fc.property(fc.integer(), (x) => x === x), { numRuns: 1000, // Number of test cases seed: 42, // Reproducible tests verbose: true, // Show details endOnFailure: false // Run all tests } );

  1. Testing Strategies by Data Type

Strings:

st.text() st.text(min_size=1, max_size=100) st.text(alphabet=st.characters(blacklist_categories=['Cs'])) st.from_regex(r'[a-z]{3,10}')

fc.string() fc.string({ minLength: 1, maxLength: 100 }) fc.hexaString() fc.asciiString() fc.unicodeString() fc.stringOf(fc.char())

Numbers:

st.integers() st.integers(min_value=0, max_value=100) st.floats(min_value=0.0, max_value=1.0) st.decimals()

fc.integer() fc.integer({ min: 0, max: 100 }) fc.float() fc.double() fc.nat()

Collections:

st.lists(st.integers()) st.lists(st.integers(), min_size=1, max_size=10) st.sets(st.text()) st.dictionaries(st.text(), st.integers()) st.tuples(st.text(), st.integers())

fc.array(fc.integer()) fc.array(fc.integer(), { minLength: 1, maxLength: 10 }) fc.set(fc.string()) fc.dictionary(fc.string(), fc.integer()) fc.tuple(fc.string(), fc.integer())

Dates:

st.datetimes() st.dates(min_value=date(2020, 1, 1)) st.times()

fc.date() fc.date({ min: new Date('2020-01-01') })

  1. Best Practices

DO:

  • Test invariants, not specific outputs

  • Use meaningful property names

  • Start with simple properties

  • Let the library shrink failures

  • Test edge cases (empty, single item, max size)

  • Combine multiple properties

  • Use preconditions (assume in Hypothesis, fc.pre in fast-check)

DON'T:

  • Test implementation details

  • Use too complex properties

  • Ignore shrinking results

  • Forget to test edge cases

  • Make properties too similar to implementation

  • Use property-based tests for everything (unit tests still valuable)

  1. Common Patterns

Metamorphic Testing:

@given(st.lists(st.integers())) def test_sort_stability(items): # Adding an element and sorting should give same order for original elements with_extra = items + [max(items) + 1] if items else [0] sorted_original = sorted(items) sorted_with_extra = sorted(with_extra)

# Original elements should appear in same relative order
assert sorted_original == [x for x in sorted_with_extra if x in sorted_original]

Differential Testing:

// Test two implementations against each other fc.assert( fc.property(fc.array(fc.integer()), (arr) => { const result1 = optimizedSort(arr); const result2 = naiveSort(arr); return JSON.stringify(result1) === JSON.stringify(result2); }) );

  1. Integration with CI/CD

pytest.ini (Hypothesis):

[pytest] addopts = --hypothesis-show-statistics --hypothesis-seed=0

[hypothesis] max_examples = 200 deadline = None

package.json (fast-check):

{ "scripts": { "test": "jest", "test:property": "jest --testNamePattern='property'", "test:verbose": "jest --verbose" } }

GitHub Actions:

name: Property Tests

on: [push, pull_request]

jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install dependencies run: npm ci - name: Run property tests run: npm run test:property

  1. Debugging Failed Properties

Hypothesis:

from hypothesis import given, strategies as st, example

@given(st.integers()) @example(0) # Add specific examples to always test @example(-1) @example(999999) def test_with_examples(x): assert process(x) >= 0

Run with verbose output

pytest --hypothesis-verbosity=verbose test_file.py

fast-check:

fc.assert( fc.property(fc.integer(), (x) => { // Use fc.pre for preconditions fc.pre(x !== 0); return 100 / x > 0; }), { seed: 1234567890, // Reproduce exact failure path: "0:1:2", // Replay specific path verbose: true } );

  1. Generate Test Report

Create a summary of property tests:

Property-Based Test Report

Coverage

  • Functions tested: 15
  • Properties verified: 42
  • Test cases generated: 50,000+
  • Edge cases found: 8

Properties Tested

sort_list

  • ✅ Preserves length
  • ✅ Creates ordered output
  • ✅ Preserves all elements
  • ✅ Handles empty lists
  • ✅ Handles duplicates

encode_decode

  • ✅ Roundtrip property (decode(encode(x)) === x)
  • ✅ Handles special characters
  • ✅ Preserves data types

merge_sorted_arrays

  • ✅ Output is sorted
  • ✅ Contains all elements
  • ✅ Length equals sum of inputs

Bugs Found

  1. Division by zero in calculation (fixed)
  2. Off-by-one error in array indexing (fixed)
  3. Unicode handling issue in string processing (fixed)

Recommendations

  • Add property tests for user authentication flow
  • Test database query builder invariants
  • Add metamorphic tests for caching layer

Checklist

  • Property-based testing library installed

  • Function invariants identified

  • Basic properties implemented

  • Edge cases covered

  • Shrinking verified

  • CI/CD integration added

  • Documentation updated

  • Team trained on property-based testing

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

threejs-scene-builder

No summary provided by upstream source.

Repository SourceNeeds Review
General

terraform-module-builder

No summary provided by upstream source.

Repository SourceNeeds Review
General

database-query-optimizer

No summary provided by upstream source.

Repository SourceNeeds Review