A comprehensive guide to Foundry's testing capabilities including Fuzz Testing and Invariant Testing
This repository demonstrates different testing methodologies in Foundry, focusing on:
- Fuzz Testing
- Invariant Testing
- Stateless vs Stateful Testing
- Handler-based Testing
MockUSDC.sol
: Basic ERC20 implementation for testingMockWETH.sol
: WETH implementation with deposit/withdrawYieldERC20.sol
: ERC20 with fee mechanism (10% fee every 10 transactions)
- Automatically generates random inputs
- Tests function behavior across many scenarios
- Example:
testInvariantBreakHard(uint256 randomAmount)
function testInvariantBreakHard(uint256 randomAmount) public {
vm.assume(randomAmount < startingAmount);
// Test deposit/withdraw cycles
}
Two main approaches:
- Stateless Testing
- Tests individual function calls
- No state maintained between calls
- Limited in catching complex bugs
// Example in StatelessFuzzCatchesTest.t.sol
- Stateful Testing
- Maintains state between function calls
- Tests sequences of operations
- Better at finding complex bugs
// Example in StatefulFuzzCatchesTest.t.sol
Located in test/invariant/HandlerStatefulFuzz/
- Handler Contract
- Controls test flow
- Bounds input values
- Manages function sequences
function depositYeildERC20(uint256 _amount) public {
uint256 amount = bound(_amount, 0, yeildERC20.balanceOf(owner));
// ... deposit logic
}
- Invariant Tests with Handler
contract InvariantBreakHardTest {
function setUp() public {
// Setup handler and target selectors
targetSelector(FuzzSelector({
addr: address(handler),
selectors: selectors
}));
}
}
- Token balances after operations
- Ownership consistency
- Fee mechanism correctness
- Deposit/Withdraw cycles
- Fee calculations
- Balance tracking
- Owner operations
# Run all tests
forge test
# Run specific test file
forge test --match-path test/invariant/HandlerStatefulFuzz/Invariant.t.sol
# Run with verbosity
forge test -vvv
- Simple fuzz testing may miss complex bugs
- Stateful testing with handlers provides better coverage
- Invariant testing helps verify persistent properties
- Handlers help control test flow and bound inputs
├── src/
│ └── HandlerStatefulFuzzCatches.sol
├── test/
│ ├── mocks/
│ │ ├── MockUSDC.sol
│ │ ├── MockWETH.sol
│ │ └── YeildERC20.sol
│ └── invariant/
│ └── HandlerStatefulFuzz/
│ ├── Handler.t.sol
│ ├── Invariant.t.sol
│ └── InvariantFail.t.sol
- Foundry
- OpenZeppelin Contracts