Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Solana development skill covering @solana/kit v5, Anchor programs, LiteSVM testing, and security patterns.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/testing.md
1---2title: Testing Strategy3description: A testing pyramid for Solana programs using LiteSVM for fast unit tests, Mollusk for isolated instruction checks, and Surfpool for integration tests with realistic state.4---56# Testing Strategy (LiteSVM / Mollusk / Surfpool)78## Testing Pyramid9101. **Unit tests (fast)**: LiteSVM or Mollusk112. **Integration tests (realistic state)**: Surfpool123. **Cluster smoke tests**: devnet/testnet/mainnet as needed1314## LiteSVM1516A lightweight Solana Virtual Machine that runs directly in your test process. Created by Aursen from Exotic Markets.1718### When to Use LiteSVM1920- Fast execution without validator overhead21- Direct account state manipulation22- Built-in performance profiling23- Multi-language support (Rust, TypeScript, Python)2425### Rust Setup2627```bash28cargo add --dev litesvm29```3031```rust32use litesvm::LiteSVM;33use solana_sdk::{pubkey::Pubkey, signature::Keypair, transaction::Transaction};3435#[test]36fn test_deposit() {37let mut svm = LiteSVM::new();3839// Load your program40let program_id = pubkey!("YourProgramId11111111111111111111111111111");41svm.add_program_from_file(program_id, "target/deploy/program.so");4243// Create accounts44let payer = Keypair::new();45svm.airdrop(&payer.pubkey(), 1_000_000_000).unwrap();4647// Build and send transaction48let tx = Transaction::new_signed_with_payer(49&[/* instructions */],50Some(&payer.pubkey()),51&[&payer],52svm.latest_blockhash(),53);5455let result = svm.send_transaction(tx);56assert!(result.is_ok());57}58```5960### TypeScript Setup6162```bash63npm i --save-dev litesvm64```6566```typescript67import { LiteSVM } from 'litesvm';68import { PublicKey, Transaction, Keypair } from '@solana/web3.js';6970const programId = new PublicKey("YourProgramId11111111111111111111111111111");71const svm = new LiteSVM();72svm.addProgramFromFile(programId, "target/deploy/program.so");7374// Build transaction75const tx = new Transaction();76tx.recentBlockhash = svm.latestBlockhash();77tx.add(/* instructions */);78tx.sign(payer);7980// Simulate first (optional)81const simulation = svm.simulateTransaction(tx);8283// Execute84const result = svm.sendTransaction(tx);85```8687### Account Types in LiteSVM8889**System Accounts:**90- Payer accounts (contain lamports)91- Uninitialized accounts (empty, awaiting setup)9293**Program Accounts:**94- Serialize with `borsh`, `bincode`, or `solana_program_pack`95- Calculate rent-exempt minimum balance9697**Token Accounts:**98- Use `spl_token::state::Mint` and `spl_token::state::Account`99- Serialize with Pack trait100101### Advanced LiteSVM Features102103```rust104// Modify clock sysvar105svm.set_sysvar(&Clock { slot: 1000, .. });106107// Warp to slot108svm.warp_to_slot(5000);109110// Configure compute budget111svm.set_compute_budget(ComputeBudget { max_units: 400_000, .. });112113// Toggle signature verification (useful for testing)114svm.with_sigverify(false);115116// Check compute units used117let result = svm.send_transaction(tx)?;118println!("CUs used: {}", result.compute_units_consumed);119```120121## Mollusk122123A lightweight test harness providing direct interface to program execution without full validator runtime. Best for Rust-only testing with fine-grained control.124125### When to Use Mollusk126127- Fast execution for rapid development cycles128- Precise account state manipulation for edge cases129- Detailed performance metrics and CU benchmarking130- Custom syscall testing131132### Setup133134```bash135cargo add --dev mollusk-svm136cargo add --dev mollusk-svm-programs-token # For SPL token helpers137cargo add --dev solana-sdk solana-program138```139140### Basic Usage141142```rust143use mollusk_svm::Mollusk;144use mollusk_svm::result::Check;145use solana_sdk::{account::Account, pubkey::Pubkey, instruction::Instruction};146147#[test]148fn test_instruction() {149let program_id = Pubkey::new_unique();150let mollusk = Mollusk::new(&program_id, "target/deploy/program");151152// Create accounts153let payer = (154Pubkey::new_unique(),155Account {156lamports: 1_000_000_000,157data: vec![],158owner: solana_sdk::system_program::ID,159executable: false,160rent_epoch: 0,161},162);163164// Build instruction165let instruction = Instruction {166program_id,167accounts: vec![/* account metas */],168data: vec![/* instruction data */],169};170171// Execute with validation172mollusk.process_and_validate_instruction(173&instruction,174&[payer],175&[176Check::success(),177Check::compute_units(50_000),178],179);180}181```182183### Token Program Helpers184185```rust186use mollusk_svm_programs_token::token;187188// Add token program to test environment189token::add_program(&mut mollusk);190191// Create pre-configured token accounts192let mint_account = token::mint_account(decimals, supply, mint_authority);193let token_account = token::token_account(mint, owner, amount);194```195196### CU Benchmarking197198```rust199use mollusk_svm::MolluskComputeUnitBencher;200201let bencher = MolluskComputeUnitBencher::new(mollusk)202.must_pass(true)203.out_dir("../target/benches");204205bencher.bench(206"deposit_instruction",207&instruction,208&accounts,209);210// Generates markdown report with CU usage and deltas211```212213### Advanced Configuration214215```rust216// Set compute budget217mollusk.set_compute_budget(200_000);218219// Enable all feature flags220mollusk.set_feature_set(FeatureSet::all_enabled());221222// Customize sysvars223mollusk.sysvars.clock = Clock {224slot: 1000,225epoch: 5,226unix_timestamp: 1700000000,227..Default::default()228};229```230231## Surfpool232233SDK and tooling suite for integration testing with realistic cluster state. Surfnet is the local network component (drop-in replacement for solana-test-validator).234235### When to Use Surfpool236237- Complex CPIs requiring mainnet programs (e.g., Jupiter with 40+ accounts)238- Testing against realistic account state239- Time travel and block manipulation240- Account/program cloning between environments241242### Setup243244```bash245# Install Surfpool CLI246cargo install surfpool247248# Start local Surfnet (use NO_DNA=1 when run by an agent)249NO_DNA=1 surfpool start250```251252### Connection Setup253254```typescript255import { Connection } from '@solana/web3.js';256257const connection = new Connection("http://localhost:8899", "confirmed");258```259260### System Variable Control261262```typescript263// Time travel to specific slot264await connection._rpcRequest('surfnet_timeTravel', [{265absoluteSlot: 250000000266}]);267268// Pause/resume block production269await connection._rpcRequest('surfnet_pauseClock', []);270await connection._rpcRequest('surfnet_resumeClock', []);271```272273### Account Manipulation274275```typescript276// Set account state277await connection._rpcRequest('surfnet_setAccount', [{278pubkey: accountPubkey.toString(),279lamports: 1000000000,280data: Buffer.from(accountData).toString('base64'),281owner: programId.toString(),282}]);283284// Set token account285await connection._rpcRequest('surfnet_setTokenAccount', [{286pubkey: ownerPubkey.toString(), // Owner of the token account (wallet)287mint: mintPubkey.toString(),288owner: ownerPubkey.toString(),289amount: "1000000",290}]);291292// Clone account from another program293await connection._rpcRequest('surfnet_cloneProgramAccount', [{294source: sourceProgramId.toString(),295destination: destProgramId.toString(),296account: accountPubkey.toString(),297}]);298```299300### SOL Supply Configuration301302```typescript303// Configure supply for economic edge case testing304await connection._rpcRequest('surfnet_setSupply', [{305circulating: "500000000000000000",306nonCirculating: "100000000000000000",307total: "600000000000000000",308}]);309```310311## Test Layout Recommendation312313```314tests/315├── unit/316│ ├── deposit.rs # LiteSVM or Mollusk317│ ├── withdraw.rs318│ └── mod.rs319├── integration/320│ ├── full_flow.rs # Surfpool321│ └── mod.rs322└── fixtures/323└── accounts.rs # Shared test account setup324```325326## CI Guidance327328```yaml329jobs:330unit-tests:331runs-on: ubuntu-latest332steps:333- uses: actions/checkout@v4334- name: Run unit tests335run: cargo test-sbf336337integration-tests:338runs-on: ubuntu-latest339needs: unit-tests340steps:341- uses: actions/checkout@v4342- name: Start Surfpool343run: NO_DNA=1 surfpool start --background344- name: Run integration tests345run: cargo test --test integration346```347348## Best Practices349350- Keep unit tests as the default CI gate (fast feedback)351- Use deterministic PDAs and seeded keypairs for reproducibility352- Minimize fixtures; prefer programmatic account creation353- Profile CU usage during development to catch regressions354- Run integration tests in separate CI stage to control runtime355