Smart contract test environment setup

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Smart contract test environment setup
Medium
~1 business day
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1238
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1167
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    867
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1080
  • image_logo-advance_0.png
    B2B Advance company logo design
    563
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    829

Setting up test environment for smart contracts

Project with mature codebase but no configured test environment — manual check of every change. New developer on team spends day to run first test. CI fell, nobody knows why. Testnet deployment instead of local — 10 minutes wait per iteration.

Test environment setup is one-time investment that pays back by second week of active development.

Framework choice for task

For Solidity projects now two real options: Foundry and Hardhat. They solve different tasks and often used together.

Parameter Foundry Hardhat
Test language Solidity TypeScript/JavaScript
Speed Very fast (revm on Rust) Slower
Fuzz-testing Built-in Only via plugins
Mainnet fork vm.createFork() --fork-url
Frontend integration Harder Easier (ethers.js)
Deploy scripts Solidity scripts TypeScript + ethers.js
Transaction debugging forge debug console.log() in contract

Our standard: Foundry for unit and fuzz tests, Hardhat for deploy scripts and frontend integration. Both configs coexist in one repo.

Project structure

project/
├── foundry.toml          # Foundry config
├── hardhat.config.ts     # Hardhat config
├── contracts/
│   ├── core/
│   └── interfaces/
├── test/
│   ├── unit/             # Foundry tests
│   ├── integration/       # Fork tests
│   └── invariant/        # Invariant tests
├── script/               # Foundry deploy scripts
├── deploy/               # Hardhat deploy scripts
└── fixtures/             # Shared fixtures

Local network

Anvil (included in Foundry) — local EVM node. Faster than Ganache, actively maintained. For development run in mainnet or testnet fork mode:

# Fork Ethereum mainnet with specific block (reproducibility)
anvil --fork-url $MAINNET_RPC --fork-block-number 19500000

# Fork with predefined accounts and balances
anvil --fork-url $MAINNET_RPC --accounts 10 --balance 10000

Fork testing — only way to check integration with Uniswap, Aave, Chainlink without testnet deployment. Transaction in local fork — instantly. On Sepolia — 12-15 seconds.

Mocks and fixtures

For test isolation use fixture hierarchy:

// BaseFixture.sol — common dependencies
abstract contract BaseFixture is Test {
    MockERC20 token;
    MockChainlinkOracle oracle;

    function setUp() public virtual {
        token = new MockERC20("Test", "TST", 18);
        oracle = new MockChainlinkOracle(2000e8); // $2000 price
    }
}

// ProtocolFixture.sol — deploy tested protocol
contract ProtocolFixture is BaseFixture {
    Protocol protocol;

    function setUp() public override {
        super.setUp();
        protocol = new Protocol(address(token), address(oracle));
    }
}

Don't use vm.mockCall for main dependencies — fragile and doesn't check interface. Create full mock contracts with minimal implementation.

CI/CD configuration

GitHub Actions config for Foundry:

name: Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Install Foundry
        uses: foundry-rs/foundry-toolchain@v1
      - name: Run unit tests
        run: forge test --match-path "test/unit/*" -vvv
      - name: Run integration tests
        run: forge test --match-path "test/integration/*" --fork-url ${{ secrets.MAINNET_RPC }}
      - name: Coverage check
        run: forge coverage --min-line-coverage 80

Separate unit and integration tests — unit tests must work without RPC keys. Integration tests only on PR to main.

Testnet configuration

For real network testing set multi-network config in Hardhat:

networks: {
  sepolia: {
    url: process.env.SEPOLIA_RPC,
    accounts: [process.env.DEPLOYER_KEY],
    chainId: 11155111,
  },
  polygon_amoy: {
    url: process.env.AMOY_RPC,
    accounts: [process.env.DEPLOYER_KEY],
    chainId: 80002,
  },
}

Faucets for main testnets: Sepolia Faucet (Alchemy/Chainlink), Amoy Faucet (official Polygon).

Timeline

Basic setup (Foundry + Hardhat, Anvil, CI) — 1 working day. With custom mocks for specific protocol and fixtures — 1-2 days. For multi-chain projects (EVM + Solana) — 2-3 days.