Core Concepts

Cheatcodes, property-based fuzzing, invariant testing, and gas-aware tracing — the ideas that make Foundry tests different from assertion-only frameworks.

Path: core-concepts

Third-party documentation. This is independently authored analysis of the public Foundry codebase — not the official docs, and not reviewed or endorsed by the Foundry team.

Core Concepts

Four ideas explain most of what makes Foundry tests look the way they do.

Cheatcodes

A cheatcode is a function call to a reserved system address that Foundry intercepts and executes on the host instead of on-chain: vm.prank(addr) changes the next call's sender, vm.warp(t) moves block timestamp, deal(addr, amt) sets a balance, vm.mockCall(...) stubs any contract function. Cheatcodes are the escape hatch from chain rules — they exist only in test/script contexts and are rejected by real networks.

Property-based fuzzing

Any zero-argument test function with parameters becomes a fuzz test: Forge generates random inputs (default hundreds of runs), asserts must hold for all of them. A failing case is shrunk toward a minimal counterexample and reported. This inverts the usual "write one example per bug" workflow — you state an invariant once and let the runner search for violations.

Invariant testing

Invariant tests go further: they run sequences of random calls against a deployed protocol world and check user-declared invariants after every call. State can be replayed, and handlers constrain which contracts may be called. This is Foundry's answer to formal-checking-lite for protocol teams.

Traces as first-class output

Every call produces a decoded trace — ABI-aware frames, gas costs per frame, revert reasons. Verbosity flags (-v through -vvvvv) control how much is printed, and forge test --gas-report aggregates per-function costs into a table. Debugging Solidity in Foundry is largely reading traces rather than stepping.