Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.
Foundry consists of:
- Forge: Ethereum testing framework (like Truffle, Hardhat and DappTools).
- Cast: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
- Anvil: Local Ethereum node, akin to Ganache, Hardhat Network.
- Chisel: Fast, utilitarian, and verbose solidity REPL.
Table tests provide a structured way to test multiple scenarios with different inputs and expected outputs. They are particularly useful for:
- Testing multiple input combinations
- Validating boundary conditions
- Testing state transitions
- Reducing test code duplication
- Inherit from
TableTest - Define your test case struct
- Create an array to store test cases
- Implement
tableLength()so the modifier can iterate through cases - Write your test function with the
tableTestmodifier
contract MyTest is TableTest {
struct Case {
uint256 input;
uint256 expected;
}
Case[] internal cases;
Case internal c;
function setupCases() public {
cases.push(Case(1, 2)); // Case 0
cases.push(Case(2, 4)); // Case 1
}
function testTable_Double() public tableTest(setupCases) {
c = cases[tableTestIndex]; // Get current case
uint256 result = c.input * 2;
assertEq(result, c.expected);
}
function tableLength() internal view override returns (uint256) {
return cases.length;
}
}
/*
Iteration Flow:
┌────────────────────┐
│ tableTest modifier │
└─────────┬──────────┘
│
▼
┌─────────────────────┐ ┌──────────-───┐
│ Case 0: input = 1 │ => │ expected = 2 │
└─────────────────────┘ └─────────-────┘
│
▼
┌─────────────────────┐ ┌────────────-─┐
│ Case 1: input = 2 │ => │ expected = 4 │
└─────────────────────┘ └────────────-─┘
Each iteration:
1. Takes snapshot
2. Runs test with current case
3. Reverts to snapshot
4. Moves to next case
*/Use table tests when you need to:
- Test multiple input/output combinations
- Validate state transitions
- Test boundary conditions
- Reduce duplicate test code
- Maintain organized test cases
- Keep test cases focused and minimal
- Use descriptive struct fields
- Group related test cases together
$ forge build$ forge test$ forge fmt$ forge snapshot$ anvil$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key>$ cast <subcommand>$ forge --help
$ anvil --help
$ cast --help