in-solana by Supanode

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

AspectDevnetLocal validator
Transaction confirmation~400ms–2sInstant
Rate limitsYes (public endpoint)No
Network conditionsRealisticArtificial
Program deploymentStandardInstant
Airdrop limit2 SOL/requestUnlimited
Account cloningBuilt-in stateOn-demand
UptimeUsually up, occasional resetsAlways 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

  1. Hardcoded mainnet addresses. Test programs should use configurable addresses. Mainnet program IDs may not exist on devnet or local validator unless cloned.
  2. Ignoring simulation. Always simulate before sending to catch errors early and save debugging time.
  3. Not testing edge cases. Test insufficient balance, wrong signer, expired blockhash, and compute budget exceeded scenarios.
  4. 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: