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:
Solana CLI: Install the Solana Command Line Interface (CLI) tools by following the instructions here.
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.