Skip to main content
Testing is a critical part of Solana program development. Anchor provides built-in support for testing your programs using TypeScript, Rust, and specialized testing frameworks. This section covers the testing strategies and tools available for Anchor programs.

Testing Strategies

There are three main approaches to testing Anchor programs:
  1. Integration Tests - Test your program by deploying it to a local validator and interacting with it through a client
  2. Unit Tests - Test individual functions and components in isolation using specialized test frameworks
  3. Rust Tests - Write tests directly in Rust using the Anchor Rust client or test frameworks

The anchor test Command

The anchor test command is the primary way to run integration tests for your Anchor programs. It automates the entire testing workflow:
anchor test
When you run anchor test on a localnet cluster, it will:
  1. Start a local Solana validator (solana-test-validator)
  2. Build your programs using anchor build
  3. Deploy your programs to the local validator
  4. Run the test files in the tests/ directory
  5. Stop the local validator

Common Options

# Skip starting a local validator (useful if already running)
anchor test --skip-local-validator

# Run tests against a specific cluster
anchor test --provider.cluster devnet

# Skip building before testing
anchor test --skip-build

# Skip deployment before testing
anchor test --skip-deploy

Manual Testing Workflow

For more control during development, you can manually run each step:
# Terminal 1: Start local validator
solana-test-validator

# Terminal 2: Build and deploy
anchor build
anchor deploy

# Run tests without starting validator
anchor test --skip-local-validator
This approach is useful when you want to:
  • Keep the validator running between test runs
  • Inspect accounts on the Solana Explorer
  • Debug transaction logs in real-time
  • Iterate quickly without restarting the validator

TypeScript Integration Tests

The default test template uses TypeScript with the Anchor client library. Tests are located in the tests/ directory.

Basic Test Structure

Here’s a simple test example:
import * as anchor from "@anchor-lang/core";
import { Program } from "@anchor-lang/core";
import { MyProgram } from "../target/types/my_program";
import { assert } from "chai";

describe("my-program", () => {
  // Configure the client to use the local cluster
  const provider = anchor.AnchorProvider.env();
  anchor.setProvider(provider);

  const program = anchor.workspace.MyProgram as Program<MyProgram>;

  it("Initializes an account", async () => {
    const myAccount = anchor.web3.Keypair.generate();

    const tx = await program.methods
      .initialize()
      .accounts({
        myAccount: myAccount.publicKey,
        user: provider.wallet.publicKey,
        systemProgram: anchor.web3.SystemProgram.programId,
      })
      .signers([myAccount])
      .rpc();

    console.log("Transaction signature:", tx);

    // Fetch and verify the account
    const account = await program.account.myAccount.fetch(
      myAccount.publicKey
    );
    assert.ok(account.initialized);
  });
});

Advanced Test Example

Here’s a more complex example testing an escrow program:
import * as anchor from "@anchor-lang/core";
import { Program } from "@anchor-lang/core";
import { TOKEN_PROGRAM_ID, Token } from "@solana/spl-token";
import { assert } from "chai";
import { Escrow } from "../target/types/escrow";

describe("escrow", () => {
  const provider = anchor.AnchorProvider.env();
  anchor.setProvider(provider);

  const program = anchor.workspace.Escrow as Program<Escrow>;
  
  let mintA: Token;
  let initializerTokenAccountA: anchor.web3.PublicKey;
  const initializerAmount = 500;

  it("Initializes escrow", async () => {
    const payer = anchor.web3.Keypair.generate();
    const mintAuthority = anchor.web3.Keypair.generate();

    // Airdrop SOL to payer
    await provider.connection.confirmTransaction(
      await provider.connection.requestAirdrop(
        payer.publicKey,
        10000000000
      )
    );

    // Create mint
    mintA = await Token.createMint(
      provider.connection,
      payer,
      mintAuthority.publicKey,
      null,
      0,
      TOKEN_PROGRAM_ID
    );

    // Create token account
    initializerTokenAccountA = await mintA.createAccount(
      provider.wallet.publicKey
    );

    // Mint tokens
    await mintA.mintTo(
      initializerTokenAccountA,
      mintAuthority.publicKey,
      [mintAuthority],
      initializerAmount
    );

    // Initialize escrow
    const escrowAccount = anchor.web3.Keypair.generate();
    
    await program.methods
      .initialize(
        new anchor.BN(initializerAmount),
        new anchor.BN(1000)
      )
      .accounts({
        initializer: provider.wallet.publicKey,
        escrowAccount: escrowAccount.publicKey,
        initializerDepositTokenAccount: initializerTokenAccountA,
        systemProgram: anchor.web3.SystemProgram.programId,
        tokenProgram: TOKEN_PROGRAM_ID,
      })
      .signers([escrowAccount])
      .rpc();

    // Verify escrow state
    const escrowState = await program.account.escrowAccount.fetch(
      escrowAccount.publicKey
    );
    assert.ok(escrowState.initializerAmount.eq(new anchor.BN(initializerAmount)));
  });
});

Rust Tests

You can write tests in Rust using the Anchor Rust client. To initialize a project with Rust tests:
anchor init --test-template rust my-program
Rust test files are located in tests/src/:
use anchor_client::{
    solana_sdk::{
        commitment_config::CommitmentConfig,
        pubkey::Pubkey,
        signature::read_keypair_file,
    },
    Client, Cluster,
};
use std::str::FromStr;

#[test]
fn test_initialize() {
    let program_id = "YourProgramID111111111111111111111111111";
    let anchor_wallet = std::env::var("ANCHOR_WALLET").unwrap();
    let payer = read_keypair_file(&anchor_wallet).unwrap();

    let client = Client::new_with_options(
        Cluster::Localnet,
        &payer,
        CommitmentConfig::confirmed(),
    );
    
    let program_id = Pubkey::from_str(program_id).unwrap();
    let program = client.program(program_id).unwrap();

    let tx = program
        .request()
        .accounts(my_program::accounts::Initialize {})
        .args(my_program::instruction::Initialize {})
        .send()
        .expect("Transaction failed");

    println!("Transaction signature: {}", tx);
}

Test Configuration

Test behavior is configured in Anchor.toml:
[provider]
cluster = "Localnet"  # or "Devnet", "Mainnet"
wallet = "~/.config/solana/id.json"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

Program Logs

When running tests, program logs are automatically streamed to:
.anchor/program-logs/<address>.<program-name>.log
You can use msg!() in your program code to add debug logging:
use anchor_lang::prelude::*;

#[program]
pub mod my_program {
    use super::*;
    
    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        msg!("Initializing with user: {}", ctx.accounts.user.key());
        Ok(())
    }
}

Specialized Testing Frameworks

For more advanced testing scenarios, Anchor supports specialized testing frameworks:
  • [LiteSVMtesting/litesvm) - Fast and lightweight in-process testing in Rust, TypeScript, and Python
  • [Mollusktesting/mollusk) - Lightweight Rust testing harness for direct SVM program execution
These frameworks offer:
  • Faster test execution (no validator startup time)
  • More control over the test environment
  • Advanced features like time travel and arbitrary account manipulation
  • Better isolation for unit testing

Best Practices

  1. Write tests early - Use Test-Driven Development (TDD) to write tests before implementing features
  2. Test edge cases - Include tests for error conditions, boundary values, and invalid inputs
  3. Use descriptive test names - Make it clear what each test is verifying
  4. Keep tests isolated - Each test should be independent and not rely on other tests
  5. Mock external dependencies - Use test accounts and fixtures instead of live accounts when possible
  6. Test on devnet before mainnet - Always test on devnet before deploying to mainnet
  7. Use continuous integration - Automate testing with CI/CD pipelines

Next Steps

  • Learn about [LiteSVMtesting/litesvm) for fast in-process testing
  • Explore [Mollusktesting/mollusk) for lightweight Rust testing
  • Read about the TypeScript Client
  • Check out the Rust Client documentation

Build docs developers (and LLMs) love