Fork Testing with Cadence
This tutorial teaches you how to run your Cadence tests against a snapshot of Flow mainnet 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 isolated local unit tests and 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
Reproducibility first
Pin a specific block height when you need reproducible results:
_10flow test --fork mainnet --fork-height <BLOCK_HEIGHT>
Document the pin heights you rely on (for example in CI variables or a simple file in the repo) and update them via a dedicated freshness PR. For best results, keep a per‑spork stable pin and also run a "latest" freshness job.
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
The test directories are already created by flow init. If needed, create the scripts directory:
_10mkdir -p cadence/scripts
First, create a script to read FlowToken supply. Create cadence/scripts/GetFlowTokenSupply.cdc:
Now create the test file cadence/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
- File paths in
Test.readFile()are relative to the test file location (use../scripts/fromcadence/tests/)
Quick verify
Run just this test file against a fork to confirm your setup works:
_10flow test cadence/tests/flow_token_test.cdc --fork mainnet
Target testnet instead:
_10flow test cadence/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.
Generate a Key Pair
Generate a key pair that will be used for your test contract's mainnet alias:
_10flow keys generate
This will output a public/private key pair. Save the private key to a file:
_10echo "YOUR_PRIVATE_KEY_HERE" > mainnet-test.pkey
Note the public key - you'll need it to derive an account address. For fork testing, any valid Flow address format works. You can use this command to generate a test address from your key:
_10flow accounts derive-address mainnet-test.pkey
Or simply use a placeholder mainnet-format address like f8d6e0586b0a20c7 for testing purposes.
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 in flow.json
Add the TokenChecker contract configuration to flow.json. The contract needs a mainnet alias so that imports can resolve properly during fork testing.
Update your flow.json to include the contract with aliases, using the address you generated in the previous step:
_20{_20 "contracts": {_20 "TokenChecker": {_20 "source": "cadence/contracts/TokenChecker.cdc",_20 "aliases": {_20 "testing": "0000000000000008",_20 "mainnet": "<from_previous_step>"_20 }_20 }_20 },_20 "accounts": {_20 "mainnet-test": {_20 "address": "<from_previous_step>",_20 "key": {_20 "type": "file",_20 "location": "mainnet-test.pkey"_20 }_20 }_20 }_20}
The Test.deployContract function will automatically deploy your contract to the testing environment during test execution.
Create Scripts for Testing
Create cadence/scripts/CheckBalance.cdc:
Create cadence/scripts/HasMinimumBalance.cdc:
Test Your Contract with Forked State
Create cadence/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 cadence/tests/token_checker_test.cdc file:
_61access(all) fun testTransactionAsMainnetAccount() {_61 // Impersonate the Flow service account (or any mainnet account)_61 // No private keys needed - fork testing has built-in impersonation_61 let serviceAccount = Test.getAccount(0x1654653399040a61)_61 _61 // Check initial balance_61 let initialBalanceScript = Test.executeScript(_61 Test.readFile("../scripts/CheckBalance.cdc"),_61 [serviceAccount.address]_61 )_61 Test.expect(initialBalanceScript, Test.beSucceeded())_61 let initialBalance = initialBalanceScript.returnValue! as! UFix64_61 _61 // Create a test recipient account and set up FlowToken vault_61 let recipient = Test.createAccount()_61 _61 // Set up the recipient's FlowToken vault_61 let setupResult = Test.executeTransaction(_61 Test.Transaction(_61 code: Test.readFile("../transactions/SetupFlowTokenVault.cdc"),_61 authorizers: [recipient.address],_61 signers: [recipient],_61 arguments: []_61 )_61 )_61 Test.expect(setupResult, Test.beSucceeded())_61 _61 // Execute transaction AS the mainnet service account_61 // This works because fork testing allows impersonating any account_61 let txResult = Test.executeTransaction(_61 Test.Transaction(_61 code: Test.readFile("../transactions/TransferTokens.cdc"),_61 authorizers: [serviceAccount.address],_61 signers: [serviceAccount],_61 arguments: [10.0, recipient.address]_61 )_61 )_61 _61 Test.expect(txResult, Test.beSucceeded())_61 _61 // Verify the sender's balance decreased_61 let newBalanceScript = Test.executeScript(_61 Test.readFile("../scripts/CheckBalance.cdc"),_61 [serviceAccount.address]_61 )_61 Test.expect(newBalanceScript, Test.beSucceeded())_61 let newBalance = newBalanceScript.returnValue! as! UFix64_61 _61 // Balance should have decreased by exactly the transfer amount_61 Test.assertEqual(initialBalance - 10.0, newBalance)_61 _61 // Verify the recipient received the tokens_61 let recipientBalanceScript = Test.executeScript(_61 Test.readFile("../scripts/CheckBalance.cdc"),_61 [recipient.address]_61 )_61 Test.expect(recipientBalanceScript, Test.beSucceeded())_61 let recipientBalance = recipientBalanceScript.returnValue! as! UFix64_61 // Recipient should have at least 10.0 (may be slightly more due to storage refunds)_61 Test.assert(recipientBalance >= 10.0, message: "Recipient should have at least 10 FLOW")_61}
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 mainnet
This runs all *_test.cdc files in your project against mainnet. You should see:
_10Test results: "cadence/tests/flow_token_test.cdc"_10- PASS: testFlowTokenSupplyIsPositive_10_10Test results: "cadence/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.
See also:
- Strategy: Testing Strategy on Flow
- Emulator (fork mode for interactive E2E): Flow Emulator
- Networks and access nodes: Flow Networks
External oracles and off-chain systems
Fork tests run against Flow chain state only:
- No live off-chain/API calls or cross-chain reads
- Price feeds, bridges, indexers, and similar must be mocked (stub contracts or fixtures)
- For end-to-end, combine with
flow emulator --forkand a local stub service
Select tests quickly
- Run specific files or directories:
_10flow test cadence/tests/flow_token_test.cdc cadence/tests/token_checker_test.cdc --fork mainnet
- Optional: narrow by function name with
--name:
_10flow test cadence/tests/token_checker_test.cdc --name _smoke --fork mainnet
- Optional: suffix a few functions with
_smokefor quick PR runs; run the full suite nightly or on protected branches.
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 emulator unit tests for determinism and isolation, 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 determinism and isolation; run forked integration tests selectively in CI
- Review the Fork Testing Flags reference for advanced options
- Learn about Flow Networks and public access nodes