Web3 Builders

Testing Smart Contracts Before Mainnet

A practical testing plan for smart contracts before mainnet, covering unit tests, fork tests, fuzzing, invariants, testnet rehearsals and deployment checks.

Lab bench with test tubes holding glowing code blocks

Smart contracts are unusual software. Once deployed, many cannot be changed, and bugs can cost real money within minutes. Traditional software teams can ship a patch the same afternoon. Contract teams often cannot. That makes testing before mainnet one of the most valuable investments a crypto project can make.

A good testing plan combines several layers, each catching different kinds of problems.

Unit tests

Unit tests check individual functions in isolation. For each function, test:

  • Normal behaviour with typical inputs
  • Edge cases, such as zero values, maximum values and empty arrays
  • Access control, confirming unauthorised callers are rejected
  • Events emitted with correct parameters
  • Reverts with the expected error messages

Aim for tests that describe intended behaviour in plain names, such as "rejects withdrawal above balance". Readable tests double as documentation for auditors.

Integration tests

Integration tests check how contracts work together. A lending protocol might test a full flow: deposit collateral, borrow, accrue interest, repay and withdraw. These tests catch mistakes in assumptions between contracts, such as one contract expecting a different decimal format from another.

Fork tests

Fork tests run your contracts against a copy of mainnet state. They let you interact with real deployed tokens, oracles and protocols without spending real funds. Fork tests reveal problems that mocks hide:

  1. Tokens with unusual behaviour, such as fees on transfer
  2. Oracle prices with real update patterns
  3. Liquidity conditions in actual pools
  4. Gas costs under realistic state sizes

If your contracts integrate with other protocols, fork tests are essential.

Fuzzing and invariant testing

Fuzzing feeds random inputs into functions to find unexpected failures. Invariant testing goes further by defining properties that must always hold, then running random sequences of actions to try to break them.

Useful invariants include:

Invariant Example
Conservation Total deposits equal the sum of user balances
Solvency Contract balance is at least total owed to users
Access Only the owner can change fees
Monotonic values Accumulated rewards never decrease
Bounds Interest rates stay within configured limits

Invariant tests find complex bugs that only appear after specific sequences of actions, which human testers rarely think to try.

Three-layer testing pyramid with small icons on each layer
Many fast unit tests at the base, fewer slow end-to-end tests at the top.

Static analysis

Static analysis tools scan code for known vulnerability patterns without running it. They flag issues such as unchecked return values, reentrancy risks and unused variables. Tools produce false positives, so review findings rather than suppressing them blindly.

Testnet rehearsals

Before mainnet, deploy the full system to a public testnet and rehearse the launch:

  • Run deployment scripts exactly as you will on mainnet
  • Verify contracts on the testnet block explorer
  • Connect the real frontend and test user flows with real wallets
  • Invite a small group of community testers
  • Test admin actions, pausing and upgrades if applicable

Rehearsals catch configuration mistakes such as wrong addresses, missing permissions or mismatched constructor parameters.

Deployment checks

On deployment day, use a checklist:

  1. Deploy from a clean, tagged commit that matches the audited code
  2. Confirm constructor parameters against a reviewed list
  3. Verify every contract on the block explorer
  4. Transfer ownership to the multisig
  5. Run post-deployment tests that read on-chain state
  6. Publish addresses on the official website

Coverage is not the goal

High test coverage numbers can hide weak tests that execute code without checking results. Focus on meaningful assertions and on the scenarios that would cost users money if they failed.

Share your testing story

Users and auditors appreciate knowing how contracts were tested. A short section in your docs describing test types, coverage of critical paths and audit history builds confidence. When you launch, keep official contract addresses and links consistent across your docs, website and profiles such as a Proud Globe pin, so users can check they are interacting with the tested contracts.

Educational content only. Nothing here is financial, legal or tax advice. Crypto assets carry risk, so check the details for your own situation.