Testing on Devnet and Local Validator
TL;DR
- - Use solana-test-validator for instant local testing with no rate limits
- - Devnet gives a more realistic network environment but has rate limits and airdrop restrictions
- - Clone mainnet accounts to local validator for testing against real program state
Testing on Solana requires a cluster where you can send transactions without risking real funds. The two primary options are a local validator (fastest iteration) and devnet (most realistic network behavior).
Local validator
The solana-test-validator runs a single-node cluster on your machine. Transactions confirm instantly, there are no rate limits, and you have full control.
Starting the validator
solana-test-validator
Default RPC endpoint: http://localhost:8899
Default WebSocket: ws://localhost:8900
Useful flags
solana-test-validator \
--reset \ # Clear ledger on start
--bpf-program <PROGRAM_ID> <SO_PATH> \ # Deploy a program at startup
--clone <ACCOUNT_ADDRESS> \ # Clone a mainnet account
--url mainnet-beta \ # Source cluster for cloning
--slots-per-epoch 32 # Shorter epochs for testing
Cloning mainnet accounts
Test against real program state by cloning accounts from mainnet:
solana-test-validator \
--url mainnet-beta \
--clone TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA \
--clone ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL \
--clone <YOUR_TARGET_ACCOUNT>
This copies the account data (balance, data, owner) from mainnet to your local validator.
Funding test wallets
solana airdrop 100 <WALLET_ADDRESS> --url localhost
Local validator airdrops are unlimited and instant.
Devnet
Devnet is a public cluster maintained by Solana that mirrors mainnet behavior with test SOL.
Configuration
solana config set --url devnet
RPC endpoint: https://api.devnet.solana.com
Getting devnet SOL
solana airdrop 2 <WALLET_ADDRESS> --url devnet
Or programmatically:
const connection = new Connection("https://api.devnet.solana.com");
const signature = await connection.requestAirdrop(
publicKey,
2 * LAMPORTS_PER_SOL,
);
await connection.confirmTransaction(signature);
Limits: 2 SOL per airdrop request, rate-limited. For larger amounts, use the Solana faucet or request from community faucets.
Devnet limitations
| Aspect | Devnet | Local validator |
|---|---|---|
| Transaction confirmation | ~400ms–2s | Instant |
| Rate limits | Yes (public endpoint) | No |
| Network conditions | Realistic | Artificial |
| Program deployment | Standard | Instant |
| Airdrop limit | 2 SOL/request | Unlimited |
| Account cloning | Built-in state | On-demand |
| Uptime | Usually up, occasional resets | Always up |
Testing patterns
Program testing with Anchor
Anchor’s test framework uses a local validator by default:
describe("my_program", () => {
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
it("initializes", async () => {
const tx = await program.methods
.initialize()
.accounts({ /* ... */ })
.rpc();
// Confirms instantly on local validator
});
});
Transaction testing
Test transaction building and signing without network interaction:
// Build transaction
const tx = new Transaction().add(instruction);
tx.recentBlockhash = blockhash;
tx.feePayer = payer.publicKey;
// Simulate without sending
const simulation = await connection.simulateTransaction(tx);
if (simulation.value.err) {
console.log("Simulation failed:", simulation.value.err);
console.log("Logs:", simulation.value.logs);
}
Simulation catches most errors (insufficient balance, wrong accounts, program errors) without consuming fees.
Testing with time-dependent logic
Local validator lets you manipulate time:
# Warp to a specific slot
solana-test-validator --warp-slot 1000000
# During runtime, advance slots:
solana slot --url localhost # Check current slot
For epoch-dependent logic, use --slots-per-epoch 32 to make epochs pass quickly.
Common testing mistakes
- Hardcoded mainnet addresses. Test programs should use configurable addresses. Mainnet program IDs may not exist on devnet or local validator unless cloned.
- Ignoring simulation. Always simulate before sending to catch errors early and save debugging time.
- Not testing edge cases. Test insufficient balance, wrong signer, expired blockhash, and compute budget exceeded scenarios.
- Skipping devnet before mainnet. Local validator testing is fast but artificial. Always verify on devnet before deploying to mainnet.
FAQ
Should I test on devnet or local validator? Both. Use local validator for fast iteration during development. Use devnet for integration testing before mainnet deployment.
Can I deploy programs to devnet?
Yes. Deploy with solana program deploy <program.so> --url devnet. Programs deployed to devnet persist across validator restarts (unlike local validator without --ledger).
Why do my devnet airdrops fail? The devnet faucet is rate-limited. Wait between requests. If you need more SOL, use community faucets or the Solana Discord faucet channel.
Can I test against mainnet without real funds?
Yes. Use simulateTransaction on mainnet to test against real state without sending transactions. For full testing, clone mainnet accounts to a local validator.
Updated: