Property-Based Testing
Testing methodology that verifies general properties or invariants hold across randomly generated inputs rather than checking specific hard-coded examples.
Property-based testing is a testing methodology where developers define properties (invariants) that must always hold true, and the testing framework automatically generates thousands of random inputs to try to falsify those properties. Unlike unit tests that verify specific input-output pairs, property-based tests explore vast state spaces to find edge cases developers would never anticipate.
Property-based testing vs unit testing
| Aspect | Unit testing | Property-based testing |
|---|---|---|
| Input selection | Developer chooses specific values | Framework generates random values |
| Coverage | Tests known scenarios | Explores unknown edge cases |
| Assertion style | "Given X, result should be Y" | "For all inputs, property P holds" |
| Bug discovery | Finds bugs in anticipated scenarios | Finds bugs in unanticipated scenarios |
| Maintenance | New test per scenario | One property covers infinite scenarios |
How it works in smart contracts
1. Define a property
Identify a fundamental truth about your system:
1// Property: total supply must equal sum of all balances2// Property: a user can always withdraw their deposited funds3// Property: no single transfer can increase total supply
2. The fuzzer generates inputs
The testing framework (e.g., Foundry) generates random:
- Function call sequences (mint, burn, transfer in random order)
- Parameter values (amounts, addresses, timestamps)
- Transaction orderings and state combinations
3. Check the property after each step
1function invariant_solvency() public view {2 // After ANY sequence of random operations,3 // the vault must always hold enough to cover deposits4 assert(5 address(vault).balance >= vault.totalDeposits()6 );7}
4. Report violations
If the fuzzer finds an input sequence that breaks the property, it reports the exact call sequence, enabling developers to reproduce and fix the bug.
Types of property-based tests
Stateless fuzzing
Each test run starts from a clean state. The fuzzer generates random inputs for a single function call:
1function testFuzz_transferNeverIncreasesSupply(2 address from,3 address to,4 uint256 amount5) public {6 uint256 supplyBefore = token.totalSupply();7 vm.prank(from);8 token.transfer(to, amount);9 assertEq(token.totalSupply(), supplyBefore);10}
Stateful invariant testing
The fuzzer generates entire sequences of function calls, building up complex state over many operations. This catches bugs that only emerge after specific transaction orderings:
1function invariant_conservationOfValue() public view {2 // Must hold after ANY sequence of deposits, withdrawals,3 // transfers, and fee accruals4 assertGe(vault.totalAssets(), vault.totalLiabilities());5}
Stateful testing is significantly more powerful for finding composite vulnerabilities in DeFi protocols.
Common properties for DeFi protocols
Token properties:
- Total supply equals sum of all balances
- Transfers preserve total supply
- Approve/transferFrom doesn't create tokens
Vault properties:
- Users can always withdraw their entitled funds
- Share price is monotonically non-decreasing (excluding losses)
- Total assets always cover total liabilities
AMM properties:
- The constant product formula holds after every swap
- Reserves match actual token balances
- Fees are always deducted, never added
Why it matters for security
Property-based testing catches vulnerability classes that unit tests systematically miss:
- Rounding errors that compound over thousands of operations
- State-dependent bugs that only trigger after specific operation sequences
- Economic exploits where invariants break under extreme market conditions
- Reentrancy paths through unexpected function call orderings
By translating security checklist items into testable properties, teams transform static documentation into active, automated verification—a core practice in defense-in-depth security workflows.
Articles Using This Term
Learn more about Property-Based Testing in these articles:
Related Terms
Fuzzing
Automated testing technique using randomly generated inputs to discover edge cases and vulnerabilities in smart contracts.
Invariant Testing
Property-based testing approach verifying that critical protocol conditions remain true across all possible execution paths.
Invariant
A property or condition that must always hold true throughout a smart contract's execution, used as a basis for testing and formal verification.
Foundry
Fast, portable Ethereum development framework written in Rust, featuring advanced testing and debugging capabilities.
Unit Test
A test that verifies the behavior of a single function or component in isolation from the rest of the system.
Need expert guidance on Property-Based Testing?
Our team at Zealynx has deep expertise in blockchain security and DeFi protocols. Whether you need an audit or consultation, we're here to help.
Get a Quote

