Fork Testing with Cadence
This tutorial teaches you how to run your Cadence tests against a snapshot of Flow mainnet or testnet using flow test --fork. You'll learn how to test your contracts against real deployed contracts and production data without needing to deploy anything to a live network or bootstrap test accounts.
Fork testing bridges the gap between fast local unit tests and expensive testnet deployments. It enables you to validate your contracts work correctly with real on-chain state, test integrations with deployed contracts, and debug issues using historical blockchain data—all in a safe, local environment.
What You'll Learn
After completing this tutorial, you'll be able to:
- Run Cadence tests against forked networks using
flow test --fork - Test contracts that depend on real mainnet contracts without manual setup
- Use account impersonation to execute transactions as any mainnet account
- Read from production blockchain state in your test suite
- Pin tests to specific block heights for historical debugging
- Integrate fork testing into your development workflow
What You'll Build
You'll create a complete fork testing setup that demonstrates:
- Reading from the live FlowToken contract on mainnet
- Deploying your own contract that interacts with mainnet contracts
- Testing custom logic against real account balances and state
- Executing transactions using impersonated mainnet accounts
- A reusable pattern for integration testing your Flow applications
Time Commitment: Approximately 30 minutes
Prerequisites
Flow CLI
This tutorial requires Flow CLI v1.8.0 or later installed. If you haven't installed it yet and have homebrew installed, run:
_10brew install flow-cli
For other operating systems, refer to the installation guide.
Basic Cadence Testing Knowledge
You should be familiar with writing basic Cadence tests. If you're new to Cadence testing, start with Testing Smart Contracts first.
Network Access
You'll need network access to Flow's public access nodes. The tutorial uses these endpoints, which are freely available:
- Mainnet:
access.mainnet.nodes.onflow.org:9000 - Testnet:
access.devnet.nodes.onflow.org:9000
This tutorial covers flow test --fork (running tests against forked network state), which is different from flow emulator --fork (starting the emulator in fork mode for manual interaction).
Create Your Project
Navigate to your development directory and create a new Flow project:
_10mkdir fork-testing-demo_10cd fork-testing-demo_10flow init --yes
The --yes flag accepts defaults non-interactively. flow init is interactive by default and can scaffold various templates.
Alternatively, create the directory and initialize in one command:
_10flow init fork-testing-demo --yes_10cd fork-testing-demo
Install Dependencies
Use the Dependency Manager to install the FlowToken and FungibleToken contracts:
_10flow dependencies install FlowToken FungibleToken
This downloads the contracts and their dependencies into the imports/ folder and updates your flow.json with the correct addresses and aliases across all networks (mainnet, testnet, emulator).
Your flow.json will now include an entry like:
_12{_12 "dependencies": {_12 "FlowToken": {_12 "source": "mainnet://1654653399040a61.FlowToken",_12 "aliases": {_12 "emulator": "0ae53cb6e3f42a79",_12 "mainnet": "1654653399040a61",_12 "testnet": "7e60df042a9c0868"_12 }_12 }_12 }_12}
Your flow.json should now have the mainnet and testnet networks configured from flow init. In fork mode, contract imports automatically resolve to the correct network addresses.
Test Reading Live State
Create the test directories:
_10mkdir -p tests cadence/scripts
First, create a script to read FlowToken supply. Create cadence/scripts/GetFlowTokenSupply.cdc:
Now create the test file tests/flow_token_test.cdc:
Notes:
- Use
Test.executeScript()to read contract state - The script imports
FlowTokenby name - the dependency manager handles address resolution - In fork mode, this automatically uses the mainnet FlowToken contract
- Extract the return value with proper type casting and assert on it
Quick verify
Run just this test file against a fork to confirm your setup works:
_10flow test tests/flow_token_test.cdc --fork
Target testnet instead:
_10flow test tests/flow_token_test.cdc --fork testnet
You should see the test PASS. If not, verify your network host in flow.json and that dependencies are installed.
Deploy and Test Your Contract
Now you'll create a contract that depends on FlowToken and test it against the forked mainnet state—no need to bootstrap tokens or set up test accounts.
Create a Test Account
Create a mainnet account for testing:
_10flow accounts create
When prompted, select mainnet as the network. This will generate a new account and save the private key to a .pkey file. Note the account address (e.g., 0xf8d6e0586b0a20c7).
Create a Contract that Uses FlowToken
Generate a new contract:
_10flow generate contract TokenChecker
This creates cadence/contracts/TokenChecker.cdc and adds it to flow.json. Now update the contract with your logic:
Configure Contract Deployment
Add the deployment configuration to flow.json. Replace YOUR_ACCOUNT_ADDRESS with the address from the previous step:
_18{_18 "contracts": {_18 "TokenChecker": {_18 "source": "cadence/contracts/TokenChecker.cdc"_18 }_18 },_18 "deployments": {_18 "mainnet": {_18 "mainnet-account": ["TokenChecker"]_18 }_18 },_18 "accounts": {_18 "mainnet-account": {_18 "address": "YOUR_ACCOUNT_ADDRESS",_18 "key": "$PRIVATE_KEY"_18 }_18 }_18}
This configuration tells the test framework to deploy TokenChecker to your mainnet account when running fork tests.
Create Scripts for Testing
Create cadence/scripts/CheckBalance.cdc:
Create cadence/scripts/HasMinimumBalance.cdc:
Test Your Contract with Forked State
Create tests/token_checker_test.cdc:
What's Happening Here
- Your contract uses FlowToken:
TokenCheckerimports and interacts with the real FlowToken contract - No bootstrapping needed: When you run with
--fork, real mainnet accounts (like0x1654653399040a61, the Flow service account) already have balances - Test against real state: You can query actual accounts and verify your contract logic works with production data
- Local deployment: Your
TokenCheckercontract is deployed locally to the test environment, but it reads from forked mainnet state
Execute Transactions with Account Impersonation
Fork testing includes built-in account impersonation—you can execute transactions as any mainnet account without needing private keys. This lets you test interactions with real accounts and their existing state.
Create Transactions
Create the transactions directory:
_10mkdir -p cadence/transactions
First, create a transaction to set up a FlowToken vault. Create cadence/transactions/SetupFlowTokenVault.cdc:
Now create the transfer transaction. Create cadence/transactions/TransferTokens.cdc:
Test Transaction Execution with Impersonation
Add this test function to the existing tests/token_checker_test.cdc file:
_60access(all) fun testTransactionAsMainnetAccount() {_60 // Impersonate the Flow service account (or any mainnet account)_60 // No private keys needed - fork testing has built-in impersonation_60 let serviceAccount = Test.getAccount(0x1654653399040a61)_60 _60 // Check initial balance_60 let initialBalanceScript = Test.executeScript(_60 Test.readFile("cadence/scripts/CheckBalance.cdc"),_60 [serviceAccount.address]_60 )_60 Test.expect(initialBalanceScript, Test.beSucceeded())_60 let initialBalance = initialBalanceScript.returnValue! as! UFix64_60 _60 // Create a test recipient account and set up FlowToken vault_60 let recipient = Test.createAccount()_60 _60 // Set up the recipient's FlowToken vault_60 let setupResult = Test.executeTransaction(_60 Test.Transaction(_60 code: Test.readFile("cadence/transactions/SetupFlowTokenVault.cdc"),_60 authorizers: [recipient.address],_60 signers: [recipient],_60 arguments: []_60 )_60 )_60 Test.expect(setupResult, Test.beSucceeded())_60 _60 // Execute transaction AS the mainnet service account_60 // This works because fork testing allows impersonating any account_60 let txResult = Test.executeTransaction(_60 Test.Transaction(_60 code: Test.readFile("cadence/transactions/TransferTokens.cdc"),_60 authorizers: [serviceAccount.address],_60 signers: [serviceAccount],_60 arguments: [10.0, recipient.address]_60 )_60 )_60 _60 Test.expect(txResult, Test.beSucceeded())_60 _60 // Verify the sender's balance decreased_60 let newBalanceScript = Test.executeScript(_60 Test.readFile("cadence/scripts/CheckBalance.cdc"),_60 [serviceAccount.address]_60 )_60 Test.expect(newBalanceScript, Test.beSucceeded())_60 let newBalance = newBalanceScript.returnValue! as! UFix64_60 _60 // Balance should have decreased by exactly the transfer amount_60 Test.assertEqual(initialBalance - 10.0, newBalance)_60 _60 // Verify the recipient received the tokens_60 let recipientBalanceScript = Test.executeScript(_60 Test.readFile("cadence/scripts/CheckBalance.cdc"),_60 [recipient.address]_60 )_60 Test.expect(recipientBalanceScript, Test.beSucceeded())_60 let recipientBalance = recipientBalanceScript.returnValue! as! UFix64_60 Test.assertEqual(10.0, recipientBalance)_60}
Key Points About Account Impersonation
- Any account can be used: Call
Test.getAccount(address)with any mainnet address - No private keys needed: Fork testing has built-in impersonation—you can sign transactions as any account
- Real account state: The account has its actual mainnet balance, storage, and capabilities
- Mutations are local: Changes only affect your test environment, not the real network
- Test complex scenarios: Impersonate whale accounts, protocol accounts, or any user to test edge cases
Run All Tests Together
Now that you have multiple test files, run them all against the forked network:
_10flow test --fork
This runs all *_test.cdc files in your project against mainnet by default. You should see:
_10Test results: "tests/flow_token_test.cdc"_10- PASS: testFlowTokenSupplyIsPositive_10_10Test results: "tests/token_checker_test.cdc"_10- PASS: testCheckBalanceOnRealAccount_10- PASS: testHasMinimumBalance_10- PASS: testTransactionAsMainnetAccount
Additional Options
You can also fork from testnet (flow test --fork testnet) or pin to a specific block height (--fork-height). See the Fork Testing Flags reference for all available options.
When to Use Fork Testing
Fork testing is most valuable for:
- Integration testing with real onchain contracts and data
- Pre-deployment validation before mainnet releases
- Upgrade testing against production state
- Reproducing issues at a specific block height
- Testing interactions with high-value or protocol accounts
- Validating contract behavior with real-world data patterns
For strategy, limitations, and best practices, see the guide: Testing Smart Contracts.
Conclusion
In this tutorial, you learned how to use fork testing to validate your Cadence contracts against live Flow network state. You created tests that read from real mainnet contracts, deployed custom contracts that interact with production data, and executed transactions using account impersonation—all without deploying to a live network or bootstrapping test accounts.
Now that you have completed this tutorial, you should be able to:
- Run Cadence tests against forked networks using
flow test --fork - Test contracts that depend on real mainnet contracts without manual setup
- Use account impersonation to execute transactions as any mainnet account
- Read from production blockchain state in your test suite
- Pin tests to specific block heights for historical debugging
- Integrate fork testing into your development workflow
Fork testing bridges the gap between local unit tests and testnet deployments, enabling you to catch integration issues early and test against real-world conditions. Use it as part of your pre-deployment validation process, alongside unit tests for speed and testnet deployments for final verification.
Next Steps
- Explore additional assertions and helpers in the Cadence Testing Framework
- Add more real-world tests that read from standard contracts like Flow NFT
- Keep unit tests on the emulator for speed and run forked integration tests selectively in CI
- Review the Fork Testing Flags reference for advanced options
- Learn about Flow Networks and public access nodes