Comparison·9 min का पठन·Solingo द्वारा

Foundry Forge बनाम Hardhat Testing — एक व्यावहारिक तुलना

Foundry Forge और Hardhat testing frameworks की detailed comparison — syntax, speed, fuzzing, debugging, और fork testing।

# Foundry Forge बनाम Hardhat Testing — एक व्यावहारिक तुलना

Smart contract testing frameworks का choice आपकी development velocity और code quality को significantly impact करता है। Hardhat लंबे समय से industry standard रहा है, लेकिन Foundry Forge एक game-changing alternative है। इस guide में हम practical examples के साथ दोनों को compare करेंगे।

Quick Overview

| Feature | Hardhat | Foundry Forge |

|---------|---------|---------------|

| Language | JavaScript/TypeScript | Solidity |

| Speed | Moderate | Fast (~10-100x) |

| Fuzzing | Limited (via plugins) | Built-in |

| Gas Reports | ✅ Good | ✅ Excellent |

| Fork Testing | ✅ Good | ✅ Excellent |

| Debugging | ✅ console.log | ✅ vm.* cheats |

| Learning Curve | Easy | Moderate |

Test Syntax Comparison

Simple Balance Test

Hardhat (JavaScript/Chai):

// test/Token.test.js

const { expect } = require("chai");

const { ethers } = require("hardhat");

describe("Token", function () {

let token;

let owner;

let addr1;

beforeEach(async function () {

[owner, addr1] = await ethers.getSigners();

const Token = await ethers.getContractFactory("Token");

token = await Token.deploy(1000);

await token.deployed();

});

it("Should assign total supply to owner", async function () {

const ownerBalance = await token.balanceOf(owner.address);

expect(ownerBalance).to.equal(1000);

});

it("Should transfer tokens", async function () {

await token.transfer(addr1.address, 50);

expect(await token.balanceOf(addr1.address)).to.equal(50);

expect(await token.balanceOf(owner.address)).to.equal(950);

});

});

Foundry Forge (Solidity):

// test/Token.t.sol

pragma solidity ^0.8.0;

import "forge-std/Test.sol";

import "../src/Token.sol";

contract TokenTest is Test {

Token token;

address owner = address(this);

address addr1 = address(0x1);

function setUp() public {

token = new Token(1000);

}

function testTotalSupplyToOwner() public {

assertEq(token.balanceOf(owner), 1000);

}

function testTransfer() public {

token.transfer(addr1, 50);

assertEq(token.balanceOf(addr1), 50);

assertEq(token.balanceOf(owner), 950);

}

}

Observations:

  • Foundry में same language (Solidity) use होती है
  • Hardhat में async/await boilerplate
  • Foundry syntax cleaner और concise

Speed Comparison

Benchmark: Uniswap V2 Tests

# Hardhat

npx hardhat test

# Time: ~45 seconds

# Tests: 120

# Foundry

forge test

# Time: ~2 seconds

# Tests: 120

# Result: Forge ~22x faster

Why Foundry is Faster?

  • Native EVM execution — No JS ↔ Solidity bridge
  • Parallel test execution — Rust concurrency
  • Minimal overhead — Direct bytecode execution
  • # Foundry parallel execution
    

    forge test --jobs 4 # 4 cores

    # Hardhat sequential (mostly)

    npx hardhat test

    Fuzzing Capabilities

    Hardhat: Manual or Plugin-Based

    // Hardhat + Echidna plugin (complex setup)
    

    // या manual random testing

    it("Fuzz transfer", async function () {

    for (let i = 0; i < 100; i++) {

    const amount = Math.floor(Math.random() * 1000);

    // test logic

    }

    });

    Foundry: Built-in Fuzzing

    contract TokenTest is Test {
    

    Token token;

    function setUp() public {

    token = new Token(1000000);

    }

    // ✅ Automatic fuzzing - runs 256 random inputs by default

    function testTransferFuzz(address to, uint256 amount) public {

    vm.assume(to != address(0));

    vm.assume(amount <= token.balanceOf(address(this)));

    uint256 balanceBefore = token.balanceOf(address(this));

    token.transfer(to, amount);

    assertEq(token.balanceOf(address(this)), balanceBefore - amount);

    }

    // Configure fuzz runs

    /// forge-config: default.fuzz.runs = 10000

    function testTransferNeverOverflows(uint256 amount) public {

    vm.assume(amount <= type(uint256).max / 2);

    uint256 balance = token.balanceOf(address(this));

    // Test logic

    }

    }

    Foundry fuzzing config:

    # foundry.toml
    

    [fuzz]

    runs = 1000 # Number of fuzz runs

    max_test_rejects = 65536

    seed = '0x123' # Reproducible fuzzing

    Advanced Testing Patterns

    1. Time Manipulation

    Hardhat:

    const { time } = require("@nomicfoundation/hardhat-network-helpers");
    
    

    it("Should allow withdrawal after timelock", async function () {

    await vault.deposit({ value: ethers.utils.parseEther("1") });

    // Fast forward 7 days

    await time.increase(7 * 24 * 60 * 60);

    await vault.withdraw();

    });

    Foundry:

    function testWithdrawAfterTimelock() public {
    

    vault.deposit{value: 1 ether}();

    // Warp time 7 days forward

    vm.warp(block.timestamp + 7 days);

    vault.withdraw();

    }

    2. Pranking (Impersonation)

    Hardhat:

    const { impersonateAccount } = require("@nomicfoundation/hardhat-network-helpers");
    
    

    it("Should allow admin to pause", async function () {

    await impersonateAccount(adminAddress);

    const admin = await ethers.getSigner(adminAddress);

    await contract.connect(admin).pause();

    });

    Foundry:

    function testAdminCanPause() public {
    

    address admin = address(0xADMIN);

    vm.prank(admin); // Next call from admin

    contract.pause();

    assertTrue(contract.paused());

    }

    // Multiple calls

    function testMultiplePranks() public {

    vm.startPrank(admin);

    contract.action1();

    contract.action2();

    vm.stopPrank();

    }

    3. Expectation Testing

    Hardhat:

    it("Should revert on insufficient balance", async function () {
    

    await expect(

    token.transfer(addr1.address, 10000)

    ).to.be.revertedWith("Insufficient balance");

    });

    Foundry:

    function testRevertInsufficientBalance() public {
    

    vm.expectRevert("Insufficient balance");

    token.transfer(addr1, 10000);

    }

    // More specific: expect exact error

    function testRevertCustomError() public {

    vm.expectRevert(

    abi.encodeWithSelector(

    Token.InsufficientBalance.selector,

    100,

    50

    )

    );

    token.transfer(addr1, 100);

    }

    Fork Testing

    Hardhat Fork Testing

    // hardhat.config.js
    

    module.exports = {

    networks: {

    hardhat: {

    forking: {

    url: "https://eth-mainnet.alchemyapi.io/v2/YOUR_KEY",

    blockNumber: 14390000

    }

    }

    }

    };

    // test

    it("Should swap on Uniswap", async function () {

    const uniswap = await ethers.getContractAt(

    "IUniswapV2Router",

    "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"

    );

    // Interact with mainnet state

    await uniswap.swapExactETHForTokens(/* ... */);

    });

    Foundry Fork Testing

    contract ForkTest is Test {
    

    uint256 mainnetFork;

    function setUp() public {

    mainnetFork = vm.createFork(

    "https://eth-mainnet.alchemyapi.io/v2/YOUR_KEY",

    14390000

    );

    vm.selectFork(mainnetFork);

    }

    function testUniswapSwap() public {

    IUniswapV2Router router = IUniswapV2Router(

    0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D

    );

    // Fork से interact करें

    router.swapExactETHForTokens{value: 1 ether}(/* ... */);

    }

    // Multiple forks

    function testCrossFork() public {

    uint256 optimismFork = vm.createFork("OPTIMISM_RPC");

    // Mainnet पर action

    vm.selectFork(mainnetFork);

    contract.doSomething();

    // Optimism पर action

    vm.selectFork(optimismFork);

    contract.doSomethingElse();

    }

    }

    Performance:

    • Hardhat fork: ~30s startup
    • Foundry fork: ~5s startup

    Debugging

    Hardhat Console.log

    // contracts/Token.sol
    

    import "hardhat/console.sol";

    function transfer(address to, uint256 amount) public {

    console.log("Transferring", amount, "to", to);

    console.log("Sender balance:", balances[msg.sender]);

    balances[msg.sender] -= amount;

    balances[to] += amount;

    }

    npx hardhat test
    

    # Output:

    # Transferring 50 to 0x1234...

    # Sender balance: 1000

    Foundry vm.* Cheats

    import "forge-std/console.sol";
    
    

    function testDebug() public {

    console.log("Address:", address(this));

    console.log("Balance:", address(this).balance);

    // Advanced debugging

    vm.expectEmit(true, true, false, true);

    emit Transfer(address(this), addr1, 100);

    token.transfer(addr1, 100);

    // Trace specific call

    vm.trace(address(token));

    token.complexFunction();

    }

    Foundry traces:

    forge test -vvvv  # Verbosity levels:
    

    # -v: Test results

    # -vv: + console.log

    # -vvv: + stack traces

    # -vvvv: + full execution traces

    Gas Reporting

    Hardhat Gas Reporter

    // hardhat.config.js
    

    require("hardhat-gas-reporter");

    module.exports = {

    gasReporter: {

    enabled: true,

    currency: 'USD',

    coinmarketcap: 'API_KEY'

    }

    };

    Output:

    ·-----------------------|----------------------------|-------------|----------------------------·
    

    | Contract · Method · Gas · USD (@ $2000/ETH) │

    ·······················|····························|·············|····························│

    | Token · transfer · 51234 · 0.10 │

    ·-----------------------|----------------------------|-------------|----------------------------·

    Foundry Gas Snapshots

    forge snapshot
    

    # Creates .gas-snapshot file

    forge snapshot --diff

    # Compare with previous snapshot

    Output:

    testTransfer() (gas: 51234)
    

    testApprove() (gas: 44567)

    Overall gas change: -1234 (-2.3%)

    // Inline gas assertions
    

    function testGasOptimization() public {

    uint256 gasBefore = gasleft();

    token.transfer(addr1, 100);

    uint256 gasUsed = gasBefore - gasleft();

    assertLt(gasUsed, 52000, "Gas usage too high");

    }

    Integration और Ecosystem

    Hardhat Advantages

  • Rich plugin ecosystem:
  • - hardhat-deploy

    - hardhat-etherscan

    - hardhat-typechain

    - hardhat-gas-reporter

  • TypeScript support: Type-safe testing
  • Mature tooling: VS Code extensions, debuggers
  • Foundry Advantages

  • All-in-one toolkit:
  • - forge (testing)

    - cast (CLI interactions)

    - anvil (local node)

    - chisel (REPL)

  • No dependencies: Rust binary, कोई node_modules नहीं
  • Lightning fast CI/CD:
  • # .github/workflows/test.yml
    
    • name: Run tests

    run: forge test

    # Completes in seconds

    Migration Path

    Hardhat → Foundry

    # 1. Install Foundry
    

    curl -L https://foundry.paradigm.xyz | bash

    foundryup

    # 2. Initialize

    forge init --force

    # 3. Install dependencies

    forge install openzeppelin/openzeppelin-contracts

    # 4. Convert tests (manual)

    # Rewrite JS tests in Solidity

    # 5. Run

    forge test

    Hybrid Approach

    // package.json
    

    {

    "scripts": {

    "test:hardhat": "hardhat test",

    "test:forge": "forge test",

    "test": "npm run test:hardhat && npm run test:forge"

    }

    }

    दोनों frameworks साथ चला सकते हैं:

    • Hardhat: Integration tests, deployment scripts
    • Foundry: Unit tests, fuzzing

    Real-World Recommendation

    Use Hardhat If:

    • Team JavaScript में comfortable है
    • Complex deployment scripts चाहिए
    • Rich plugin ecosystem important है
    • TypeScript typing चाहिए

    Use Foundry If:

    • Speed critical है (CI/CD में especially)
    • Fuzzing important है
    • Solidity-only development prefer करते हैं
    • Minimal dependencies चाहिए

    Best of Both Worlds:

    ├── test/
    

    │ ├── foundry/ # Unit tests, fuzzing

    │ │ ├── Token.t.sol

    │ │ └── Vault.t.sol

    │ └── hardhat/ # Integration tests

    │ ├── deploy.test.js

    │ └── integration.test.js

    ├── foundry.toml

    └── hardhat.config.js

    Conclusion

    Hardhat और Foundry दोनों powerful हैं, लेकिन different use cases के लिए।

    Key takeaways:

    • Foundry: Faster, built-in fuzzing, Solidity-native
    • Hardhat: Mature ecosystem, easier onboarding, TypeScript
    • Speed difference: Foundry ~10-100x faster
    • Best approach: Consider hybrid setup

    नए projects में Foundry try करें — speed difference alone worth it है। Existing Hardhat projects में gradual migration consider करें।

    Testing framework आपकी development velocity define करती है। Choose wisely!

    Practice में लगाने के लिए तैयार हैं?

    Solingo पर interactive exercises के साथ इन concepts को apply करें।

    मुफ्त में शुरू करें