Skip to content
Back to skills

Forge Fuzz Testing

ASecurity

Property-based testing with Foundry's fuzzer. Use when writing fuzz tests, invariant tests, or testing edge cases. Covers fuzz test structure, input constraints, fixtures, and invariant testing patterns.

  • 9 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 10, 2026
testingbashtestingdebuggingsecurity

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned September 10, 2026

npx -y skills add cyotee/indexedex --skill forge-fuzz-testing --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Forge Fuzz Testing?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Forge Fuzz Testing
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/cyotee-forge-fuzz-testing/badge)](https://www.skillsdirectory.com/skills/cyotee-forge-fuzz-testing)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: forge-fuzz-testing
description: Property-based testing with Foundry's fuzzer. Use when writing fuzz tests, invariant tests, or testing edge cases. Covers fuzz test structure, input constraints, fixtures, and invariant testing patterns.
---

# Forge Fuzz Testing

Property-based testing using Foundry's built-in fuzzer to test contract behavior across many inputs.

## When to Use

- Testing mathematical operations for edge cases
- Finding unexpected inputs that break invariants
- Testing boundary conditions automatically
- Verifying properties hold across all valid inputs
- Security testing for unexpected behaviors

## Quick Start

Any test function with parameters becomes a fuzz test:

```solidity
// Regular test - one specific case
function test_Withdraw() public {
    target.deposit{value: 1 ether}();
    target.withdraw(1 ether);
}

// Fuzz test - 256 random cases by default
function testFuzz_Withdraw(uint256 amount) public {
    vm.deal(address(this), amount);
    target.deposit{value: amount}();
    target.withdraw(amount);
    assertEq(address(target).balance, 0);
}
```

## Interpreting Results

```
[PASS] testFuzz_Withdraw(uint256) (runs: 256, μ: 28453, ~: 28453)
```

| Symbol | Meaning |
|--------|---------|
| `runs` | Number of test cases executed |
| `μ` (mu) | Mean gas consumption |
| `~` (tilde) | Median gas consumption |

## Input Constraints

### Using vm.assume

Filter out invalid inputs:

```solidity
function testFuzz_Divide(uint256 a, uint256 b) public {
    vm.assume(b != 0); // Skip division by zero
    uint256 result = target.divide(a, b);
    assertEq(result, a / b);
}
```

### Using bound

Constrain inputs to a range (preferred over assume for efficiency):

```solidity
function testFuzz_Deposit(uint256 amount) public {
    // Bound amount between 1 wei and 100 ether
    amount = bound(amount, 1, 100 ether);

    vm.deal(address(this), amount);
    target.deposit{value: amount}();
    assertEq(target.balanceOf(address(this)), amount);
}
```

### Combining Constraints

```solidity
function testFuzz_Transfer(address to, uint256 amount) public {
    // Filter invalid addresses
    vm.assume(to != address(0));
    vm.assume(to != address(target));

    // Bound amount
    amount = bound(amount, 1, token.balanceOf(address(this)));

    token.transfer(to, amount);
}
```

## Test Fixtures

Define specific values that must be tested:

### Array Fixtures

```solidity
// These values will definitely be tested
uint256[] public fixtureAmount = [0, 1, type(uint256).max];
address[] public fixtureRecipient;

constructor() {
    fixtureRecipient.push(address(0));
    fixtureRecipient.push(address(1));
}

function testFuzz_EdgeCases(uint256 amount, address recipient) public {
    // Fuzzer includes fixture values plus random values
}
```

### Function Fixtures

```solidity
function fixtureAmount() public pure returns (uint256[] memory) {
    uint256[] memory amounts = new uint256[](3);
    amounts[0] = 0;
    amounts[1] = 1;
    amounts[2] = type(uint256).max;
    return amounts;
}
```

## Configuration

### foundry.toml Settings

```toml
[fuzz]
runs = 256              # Number of fuzz runs (default: 256)
max_test_rejects = 65536  # Max rejected inputs before failing
seed = "0x1234"         # Deterministic seed for reproducibility
dictionary_weight = 40  # Weight for dictionary-based inputs
```

### Per-Test Configuration

```solidity
/// forge-config: default.fuzz.runs = 1000
function testFuzz_HighRuns(uint256 x) public {
    // This test runs 1000 times
}
```

## Invariant Testing

See [invariants.md](invariants.md) for comprehensive invariant testing patterns.

### Basic Structure

```solidity
contract MyInvariantTest is Test {
    MyContract target;
    Handler handler;

    function setUp() public {
        target = new MyContract();
        handler = new Handler(target);

        // Tell fuzzer which contract to call
        targetContract(address(handler));
    }

    // Invariant functions start with "invariant_"
    function invariant_TotalSupplyMatchesBalances() public {
        uint256 totalFromBalances = handler.sumOfAllBalances();
        assertEq(target.totalSupply(), totalFromBalances);
    }
}

// Handler wraps calls to bound inputs
contract Handler is Test {
    MyContract target;
    address[] public actors;

    constructor(MyContract _target) {
        target = _target;
        actors.push(makeAddr("alice"));
        actors.push(makeAddr("bob"));
    }

    function deposit(uint256 actorIndex, uint256 amount) public {
        actorIndex = bound(actorIndex, 0, actors.length - 1);
        amount = bound(amount, 0, 10 ether);

        address actor = actors[actorIndex];
        vm.deal(actor, amount);
        vm.prank(actor);
        target.deposit{value: amount}();
    }
}
```

## Common Fuzz Testing Patterns

### Testing Mathematical Properties

```solidity
// Commutative property
function testFuzz_AdditionCommutative(uint128 a, uint128 b) public {
    assertEq(a + b, b + a);
}

// Associative property
function testFuzz_AdditionAssociative(uint64 a, uint64 b, uint64 c) public {
    assertEq((a + b) + c, a + (b + c));
}

// Identity property
function testFuzz_MultiplicationIdentity(uint256 a) public {
    assertEq(a * 1, a);
}
```

### Testing Reversibility

```solidity
function testFuzz_DepositWithdrawRoundTrip(uint256 amount) public {
    amount = bound(amount, 1, 100 ether);
    vm.deal(address(this), amount);

    uint256 balanceBefore = address(this).balance;

    target.deposit{value: amount}();
    target.withdraw(amount);

    assertEq(address(this).balance, balanceBefore);
}
```

### Testing Encoding/Decoding

```solidity
function testFuzz_EncodeDecode(
    address addr,
    uint256 amount,
    bytes32 data
) public {
    bytes memory encoded = target.encode(addr, amount, data);
    (address decodedAddr, uint256 decodedAmount, bytes32 decodedData) =
        target.decode(encoded);

    assertEq(decodedAddr, addr);
    assertEq(decodedAmount, amount);
    assertEq(decodedData, data);
}
```

### Testing Against Reference Implementation

```solidity
function testFuzz_MatchesReference(uint256 a, uint256 b) public {
    vm.assume(b != 0);

    uint256 optimized = target.optimizedDivide(a, b);
    uint256 reference = a / b; // Simple reference

    assertEq(optimized, reference);
}
```

## Running Fuzz Tests

```bash
# Run all tests (including fuzz)
forge test

# Run with more fuzz runs
forge test --fuzz-runs 1000

# Run specific fuzz test
forge test --match-test testFuzz_Withdraw

# Run with seed for reproducibility
forge test --fuzz-seed 0x1234

# Show fuzz input on failure
forge test -vvvv
```

## Debugging Failures

When a fuzz test fails, Foundry shows the failing input:

```
[FAIL. Reason: assertion failed; counterexample: calldata=0x... args=[12345]]
```

### Reproduce the failure

```solidity
function test_ReproduceFailure() public {
    // Use the exact failing input
    testFuzz_MyFunction(12345);
}
```

### Shrinking

Foundry automatically tries to find the smallest failing input.

Files in this skill

  • SKILL.md6.9 KB
  • invariants.md8.9 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…