Testing Solana Programs (Anchor) with Mocha

Testing Solana Programs (Anchor) with Mocha

Introduction

Testing your programs is essential to ensure the reliability and functionality of your code and ensure it performs as intended. In this article, we will provide a comprehensive guide on writing tests using Mocha JS. We'll focus on a program developed by DataKnox in Anchor, a specialized Rust framework for Solana programs.

Here is what the program looks like:

use anchor_lang::prelude::*;

declare_id!("ByxaKZWC2QqigDuUjxH6nMv3MdVgBNzbpdRmiEE3ssn9");

#[program]
pub mod flipper {
    use super::*;

    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        let switchAccount = &mut ctx.accounts.switchAccount;
        switchAccount.state = true;
        Ok(())
    }

    pub fn flip(ctx: Context<Flip>) -> Result<()> {
        let switchAccount = &mut ctx.accounts.switchAccount;
        if switchAccount.state {
            switchAccount.state = false
        } else {
            switchAccount.state = true
        }
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = user, space = 16 + 16)]
    pub switchAccount: Account<'info, SwitchAccount>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct Flip<'info> {
    #[account(mut)]
    pub switchAccount: Account<'info, SwitchAccount>,
} 

#[account]
pub struct SwitchAccount {
    pub state: bool,
}

The first step would be to run anchor build to compile the program and generate a keypair that can be used to deploy it.

We also need to get our program ID (an address for the program that our program can be identified with) using the command:

solana address -k target/deploy/flipper-keypair.json. Replace flipper with the name of your program.

In our code, the program ID needs to go in two places:

  • The declare_id! macro at the top of our program’s source code.

  • The Anchor.toml file under the key with the same name as your program.

declare_id!("ByxaKZWC2QqigDuUjxH6nMv3MdVgBNzbpdRmiEE3ssn9");
[programs.localnet]
flipper = "ByxaKZWC2QqigDuUjxH6nMv3MdVgBNzbpdRmiEE3ssn9"

Prerequisites

Before we dive into testing, make sure you have the following prerequisites in place:

  1. Solana CLI: Install the Solana Command Line Interface (CLI) tools by following the instructions here.

  2. Node.js and NPM: Ensure you have Node.js and NPM installed on your system. You can download them from nodejs.org.

Setting our Solana Environment

This process involves using a local Solana validator to simulate the Solana blockchain network using the Solana CLI to provide practical insights into the program's behavior.

Generate a Keypair:

We start by generating a key pair for the dev environment by using the command solana-keygen new and you will get an address that you can use to interact with the blockchain.

Configure RPC-URL:

For our local testing, we need to set the RPC-URL to localhost using the command: solana config set --url localhost.

The output should look like this:

Start the Test Validator

The next step would be to start our test validator using the command solana-test-validator. This would help simulate the Solana blockchain locally.

With our test validator running, we can now make requests (like request airdrop, get details about our address, deploy and test our program) to our local blockchain.

Deploy to Localhost

With solana-test-validator running on your local machine, you can deploy the contract with the command: anchor deploy.

Writing the Test in Mocha

When the anchor build command is executed, Anchor automatically generates a test file for us, complete with a basic scaffold for the code. The test is written in Mocha, the it functions are used for the individual tests, and the describe function helps organize and group related tests together.

//Import necessary libraries
const anchor = require("@coral-xyz/anchor");
const assert = require("assert");
const { SystemProgram } = anchor.web3;

// Set up Mocha test suite
describe("flipper", () => {
  // Configure the client to use the local cluster.
  const provider = anchor.AnchorProvider.env();
  anchor.setProvider(provider);
  const program = anchor.workspace.Flipper;
});

We then have two tests for each of our instructions from the flipper program.

Testing the Initialize Instruction

The first test calls the initialize instruction of the flipper program, then fetches the account data and checks if the property of the account is true.

it("Creates and Flip Account", async () => {
    // Generate a keypair
    const switchAccount = anchor.web3.Keypair.generate();
    // Call the initialize function
    await program.methods
      .initialize()
      .accounts({
        switchAccount: switchAccount.publicKey,
        user: provider.wallet.publicKey,
        systemProgram: SystemProgram.programId,
      })
      .signers([switchAccount])
      .rpc();
    // Fetch account data
    const baseAccount = await program.account.switchAccount.fetch(
      switchAccount.publicKey
    );
    assert.ok(baseAccount.state);
    _baseAccount = baseAccount;
  });

Testing the Flip Instruction

The next test calls the flip instruction of the flipper program, specifying the switchAccount as the only account that is needed. Finally, it gets the switchAccount from the Solana cluster and asserts that its state is false.

it("Flip It", async () => {
    const baseAccount = _baseAccount;
    // Call the flip function
    await program.methods
      .flip()
      .accounts({
        accounts: {
          dataAccount: baseAccount.publicKey,
        },
      })
      .rpc();
    // Fetch the account data
    const account = await program.account.dataAccount.fetch(
      baseAccount.publicKey
    );
    assert.ok(account.state == false);
  });

Running the Tests

You can run the tests for your Solana program using the following command:

anchor test

This command will execute your Mocha tests and provide feedback on the program's behavior.

Conclusion

Overall, testing is an essential part of the software development. Testing programs can help to ensure that our codes are of the highest quality and that they meet the intended needs.

To delve deeper into the Solana ecosystem or embark on your journey of building on Solana, the Solana Dev Resource is an excellent starting point.