# Recommended: Use yarn script (sets profile automatically)yarn test-evm-foundry# Alternative: Set profile manuallyFOUNDRY_PROFILE=local-test forge test
Prefer vm.mockCall over custom mocks for simple return values. This is the recommended pattern in Across Protocol.
function testWithMockCall() public { address fakeContract = makeAddr("fakeContract"); // Bypass extcodesize check (required for mock calls) vm.etch(fakeContract, hex"00"); // Mock the return value vm.mockCall( fakeContract, abi.encodeWithSelector(IERC20.balanceOf.selector, address(this)), abi.encode(1000) ); // Verify the call is made with expected parameters vm.expectCall( fakeContract, 0, // msg.value abi.encodeWithSelector(IERC20.transfer.selector, recipient, amount) ); // Execute your test myContract.transferTokens(fakeContract, recipient, amount);}
function test_EnableL1TokenForLiquidityProvision() public { // Before enabling, lpToken should be zero address (address lpTokenBefore, , , , , ) = fixture.hubPool.pooledTokens(address(fixture.weth)); assertEq(lpTokenBefore, address(0), "lpToken should be zero before enabling"); // Enable the token fixture.hubPool.enableL1TokenForLiquidityProvision(address(fixture.weth)); // After enabling, verify the pooledTokens struct (address lpToken, bool isEnabled, uint32 lastLpFeeUpdate, , , ) = fixture.hubPool.pooledTokens(address(fixture.weth)); assertTrue(lpToken != address(0), "lpToken should not be zero after enabling"); assertTrue(isEnabled, "isEnabled should be true"); assertEq(lastLpFeeUpdate, block.timestamp, "lastLpFeeUpdate should be current time");}function test_EnableL1Token_RevertsIfNotOwner() public { vm.prank(otherUser); vm.expectRevert("Ownable: caller is not the owner"); fixture.hubPool.enableL1TokenForLiquidityProvision(address(fixture.weth));}
Events emit from HubPool’s address, not the adapter
vm.expectRevert() may lose error data
Use vm.expectEmit() carefully with correct address
// Expect event from HubPool, not adaptervm.expectEmit(address(hubPool));emit TokensRelayed(...);hubPool.relaySpokePoolAdminFunction(chainId, message);
# Run all local testsFOUNDRY_PROFILE=local-test forge test# Run specific contractFOUNDRY_PROFILE=local-test forge test --match-contract HubPool_AdminTest# Run specific testFOUNDRY_PROFILE=local-test forge test --match-test testDeposit# Verbose output (useful for debugging)FOUNDRY_PROFILE=local-test forge test -vvv# Very verbose (shows trace for all tests)FOUNDRY_PROFILE=local-test forge test -vvvv
# Show gas usage for all testsFOUNDRY_PROFILE=local-test forge test --gas-report# Save gas snapshot for comparisonFOUNDRY_PROFILE=local-test forge snapshot# Compare against saved snapshotFOUNDRY_PROFILE=local-test forge snapshot --diff
# Show detailed traces (-vvvv)FOUNDRY_PROFILE=local-test forge test --match-test testFailingTest -vvvv# Show stack traces for revertsFOUNDRY_PROFILE=local-test forge test --match-test testRevert -vvv
Do not modify foundry.toml without asking. The configuration is optimized for the project’s specific needs.
Key settings for local-test profile:
[profile.local-test]test = "test/evm/foundry/local" # Only local tests, no forksrevert_strings = "default" # Full revert messages for debuggingcache_path = "cache-foundry-local"out = "out-local"