Skip to main content
ComposableCoW follows Foundry best practices with unit tests, fuzz tests, and fork tests to ensure contract reliability and security.

Test Structure

The project includes three types of tests:
  • Unit Tests: Test individual contract functions in isolation
  • Fuzz Tests: Randomized input testing to discover edge cases
  • Fork Tests: End-to-end testing against mainnet contract forks

Prerequisites

Before running tests, ensure you have:
  1. Foundry installed
  2. Environment variables configured (copy .env.example to .env)
  3. An Ethereum RPC URL for fork tests (archive node recommended)
.env
ETH_RPC_URL=http://your-archive-node:8545
PRIVATE_KEY=your_private_key_here
SETTLEMENT=0x9008D19f58AAbD9eD0D60971565AA8510560ab41
Fork tests require ETH_RPC_URL to be set to an archive node for full historical state access.

Running Tests

Unit Tests Only

Run basic unit tests without fuzz or fork tests:
forge test -vvv --no-match-test "fork|[fF]uzz"
This excludes both fork tests and fuzz tests, running only isolated unit tests.

Unit + Fuzz Tests

Include fuzzing tests but skip fork tests:
forge test -vvv --no-match-test "fork"
Fuzz tests include simulate functions that run full end-to-end integration testing, including conditional order settlement.

All Tests (Unit + Fuzz + Fork)

Run the complete test suite including fork tests:
forge test -vvv
Fork tests simulate against production Ethereum mainnet contracts and require an archive node RPC endpoint.

Test Verbosity

Control test output detail with verbosity flags:
  • -v: Show test results
  • -vv: Show test results and console.log output
  • -vvv: Show test results, logs, and stack traces for failing tests
  • -vvvv: Show stack traces for all tests and setup
  • -vvvvv: Show full stack traces with decoded data
# Minimal output
forge test -v

# Detailed output with stack traces
forge test -vvvv

Test Coverage

Generate a coverage report for the test suite:
forge coverage -vvv --no-match-test "fork" --report summary
Fork tests are excluded from coverage reports to avoid external contract noise.
For detailed coverage output:
# Generate detailed HTML coverage report
forge coverage --no-match-test "fork" --report lcov
genhtml lcov.info -o coverage/

Test Organization

Tests are organized by functionality:
test/
├── Base.t.sol                      # Base test setup and utilities
├── ComposableCoW.t.sol             # Core ComposableCoW tests
├── ComposableCoW.base.t.sol        # Shared test infrastructure
├── ComposableCoW.twap.t.sol        # TWAP order type tests
├── ComposableCoW.gat.t.sol         # GoodAfterTime tests
├── ComposableCoW.stoploss.t.sol    # StopLoss tests
├── ComposableCoW.tat.t.sol         # TradeAboveThreshold tests
├── ComposableCoW.guards.t.sol      # Guard functionality tests
├── ComposableCoW.forwarder.t.sol   # Order forwarding tests
├── helpers/                         # Test helper contracts
│   ├── Safe.t.sol
│   ├── CoWProtocol.t.sol
│   └── Tokens.t.sol
└── libraries/                       # Test libraries
    ├── ComposableCoWLib.t.sol
    ├── SafeLib.t.sol
    └── TestAccountLib.t.sol

Writing Tests

Tests inherit from Base.t.sol which provides:
  • Test accounts (alice, bob, carol)
  • Pre-deployed Safe contracts
  • CoW Protocol infrastructure
  • Token contracts and balances

Example Unit Test

contract MyTest is BaseComposableCoWTest {
    function setUp() public override {
        super.setUp();
    }

    function test_createOrder() public {
        // Test implementation
        IConditionalOrder.ConditionalOrderParams memory params = 
            IConditionalOrder.ConditionalOrderParams({
                handler: address(twapHandler),
                salt: keccak256("unique-salt"),
                staticInput: abi.encode(twapData)
            });
        
        composableCow.create(params, true);
        
        // Assertions
        assertTrue(composableCow.singleOrders(address(safe1), orderHash));
    }
}

Example Fuzz Test

function testFuzz_setRoot(address owner, bytes32 root) public {
    vm.assume(owner != address(0));
    
    composableCow.setRoot(root, ComposableCoW.Proof({location: 0, data: ""}));
    
    assertEq(composableCow.roots(owner), root);
}

Continuous Integration

The project includes a CI profile in foundry.toml:
[profile.ci]
verbosity = 3
Use this profile in CI/CD pipelines:
FOUNDRY_PROFILE=ci forge test --no-match-test "fork"

Debugging Tests

Using Console Logs

import {console} from "forge-std/console.sol";

function test_debug() public {
    console.log("Order hash:", orderHash);
    console.logAddress(safe1);
}

Using Traces

Inspect call traces for failing tests:
forge test --match-test test_failing -vvvv

Gas Snapshots

Create gas usage snapshots:
forge snapshot --no-match-test "fork"
Compare gas usage:
forge snapshot --diff .gas-snapshot

Common Test Patterns

Testing Reverts

function test_revertOnInvalidParams() public {
    vm.expectRevert(ComposableCoW.InvalidParams.selector);
    composableCow.create(invalidParams, true);
}

Testing Events

function test_emitsEvent() public {
    vm.expectEmit(true, true, true, true);
    emit ConditionalOrderCreated(address(safe1), orderHash);
    
    composableCow.create(params, true);
}

Testing with Time Warps

function test_orderValidAfterTime() public {
    // Move forward 1 hour
    vm.warp(block.timestamp + 3600);
    
    (GPv2Order.Data memory order,) = 
        composableCow.getTradeableOrderWithSignature(
            address(safe1), params, bytes(""), new bytes32[](0)
        );
    
    assertEq(order.validTo, block.timestamp + 1800);
}

Performance Optimization

Speed up test execution:
# Run tests in parallel
forge test --no-match-test "fork" -j 4

# Skip specific slow tests
forge test --no-match-test "fork|slow"

Integration Testing

For integration testing with external services like Watch Tower, see the local deployment guide.

Build docs developers (and LLMs) love