Speed up Solana program tests with Jest and Bankrun | Solana (2024)

Testing your Solana programs is a critical part of the development process toensure that your program behaves as expected and can even speedup yourdevelopment. This guide will walk you through how you can test your Solanaprograms using Bankrun, a superfast test runner for Solana programs. .

Most Solana tests use the Mocha framework for writingthe tests and Chai for assertions. However, you canuse any testing framework that you are comfortable with. In this guide we willhave a look at Jest andBankrun. With Bankrun, you canaccelerate your tests by almost 10x, gain the ability to modify program time,and write custom account data.

Presets #

There are a few presets that will set you up with a basic testing environmentfor your Solana programs. These presets are for example:

npx create-solana-dapp my-dapp

create-solana-dapp will set you up with a Solana web project with variousconfiguration options, including a Next.js or React client, the Tailwind UILibrary, and a simple Anchor program. The tests are written using Jest and canbe run with the anchor test command.

npx create-solana-game my-game

create-solana-game will set you up with a Solana game project that includesJest, Mocha andBankrun tests and a NextJS appapp and an additional Unity Game engine client using Solana Wallet adapter. TheMocha and Bankrun tests can both be run using the anchor test command.

You can also find many test examples in theSolana Program Examples.

Anchor test #

Using the Anchor framework, you can run anchor test command to perform thepre-configured test command within the anchor.toml file.

Anchor.toml

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

This will run the tests in the tests directory using the ts-mocha command witha timeout of 1,000,000 milliseconds.

What anchor does here is that it starts up a local validator, deploys theprogram from your anchor workspace and runs the test against the defined networkin the Anchor.toml.

Info

Tip: You can also run anchor test --detach to let the validator continuerunning after the tests have finished which lets you inspect your transactionsin the Solana Explorer.

You can also define your own test command in the anchor.toml file. For exampleyou can first run the mocha tests against the local validator and then run thejest tests using bankrun by combining them using &&:

This would run the mocha tests first and then run the jest tests.

You can also define your own test command. For example:

Anchor.toml

super_test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 only_super_test.ts"jest_tests = "yarn test"

These you would then run using:

anchor run super_testanchor run jest_tests

Info

Note though that in this case anchor does not start a local validator for you.So you will need to deploy the program yourself to your local cluster or runthem against a public test network. Also Anchor environment variables willonly be available when run trough the anchor test commands not when runningtests with yarn test for example.

Migrating from Mocha to Jest #

In this part we will learn how to migrate from Mocha toJest. Jest is another Javascript testing framework similarto Mocha. It already has an integrated test runner so you don't need to use Chaianymore.

First you need to install Jest:

yarn add --dev jest

Then you add a new command to your package.json:

package.json

{ "scripts": { "test": "jest" }}

Then you can run the tests with:

yarn test

Since we want to run our tests with Typescript we need to install the ts-jestpackage and also create a Jest configuration file:

yarn add --dev ts-jest @jest/globals @types/jestyarn ts-jest config:init

This will create a jest.config.js file in your project. Now you can updateyour Anchor.toml file to run the Jest tests:

Anchor.toml

test = "yarn test"

Jest Troubleshooting #

  1. In case you get aSyntaxError: Cannot use import statement outside a module
    error, you either did not create a jest config or you need to add the
    following to your jest.config.js file:

jest.config.js

module.exports = { transform: { "^.+\\.tsx?$": "ts-jest", }, testEnvironment: "node", moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],};
  1. Since running tests against the local validator can take quite some time, ifyou get an error saying that you can not log after the test is finished yourtest is probably timing out. Different from Mocha, Jest does not have adefault timeout. You can set a timeout in your jest.config.js file:

jest.config.js

module.exports = { testTimeout: 10000,};

Or you can set a timeout for a single test:

your.test.js

test("test name", async () => { // your test code}, 10000);
  1. If you get an error saying that Anchor environment variables are missing youare probably trying to use the AnchorProvider without running the teststhrough anchor test or anchor run test. Just update your Anchor.toml torun the Jest tests yarn test to run them in the anchor environment with allthe environment variables set.

Bankrun #

Instead of using solana-test-validator you can also useSolana Bankrun. It actssimilarly to the local validator but is more lightweight and faster. Some reallyhelpful features arewriting custom account dataandtime travelwhich makes it much easier to test time based programs and programs that rely onspecific account data.

To use Bankrun and Bankrun Anchor you need to add it to your package.json:

yarn add solana-bankrun anchor-bankrun

To switch from a Mocha Anchor test to aBankrun Anchor test you onlyneed to change the provider to be aBankrunProviderand create a context using startAnchor within each of your test files:

my-test.test.ts

// ... imports heredescribe('My test', () => { test('Test allocate counter + increment tx', async () => { const context = await startAnchor(".", [], []); const client = context.banksClient;  const provider = new BankrunProvider(context); anchor.setProvider(provider);  // ... testing logic }}

startAnchor will automatically add your program from your Anchor workspace tothe Bankrun bank. You can alsoadd additional accounts and programsto the bank by passing them in the startAnchor function. There are a fewthings that are different to running tests against the local validator thoughwhich we will cover in the next section.

Bankrun differences to the local validator #

Bankrun uses a BanksServer (a simplified version ofSolana's bankthat processes transactions and manages accounts) and a BanksClient to simulatethe Solana network, enabling advanced testing features like time manipulationand dynamic account data setting. Unlike the solana-test-validator, bankrunallows efficient testing by running against a local instance of the networkstate, making it ideal for testing Solana programs without the overhead of afull validator. The behaviour for transactions is pretty similar, but there area few differences to the local validator:

Airdrops #

  • Bankrun does not support Airdrops. The standard signer used in theBankrunProvider will be automatically funded with some Sol. If you needanother funded account you can create one by passing in an additional accountin the startAnchor function.
let secondKeypair: Keypair = new anchor.web3.Keypair(); context = await startAnchor( "",[], [ { address: secondKeypair.publicKey, info: { lamports: 1_000_000_000, // 1 SOL equivalent data: Buffer.alloc(0), owner: SYSTEM_PROGRAM_ID, executable: false, }, }, ]);provider = new BankrunProvider(context);

Confirming transactions #

Since Bankrun is directly working on the bank you will not need to confirm yourtransactions. So the connection.confirmTransaction() function will not beavailable. You can just leave it out.

Getting account data #

While you can still use connection.getAccount to retrieve account data, thepreferred method in the bankrun framework is to use client.getAccount, whichreturns a Promise<Account>. This method aligns better with the testingframework's design. However, if you prefer consistency with how accounts areretrieved in the rest of your Solana codebase, you can continue usingconnection.getAccount. Choose the method that best fits your specific use case.

await client.getAccount(playerPDA).then(info => { const decoded = program.coder.accounts.decode( "playerData", Buffer.from(info.data), ); console.log("Player account info", JSON.stringify(decoded)); expect(decoded).toBeDefined(); expect(parseInt(decoded.energy)).toEqual(99);});

Signing transactions with another keypair #

By default when using program.function.rpc() the transaction will beautomatically signed with the provider.wallet keypair. If you want to sign thetransaction with another keypair you can create a second provider and then usethat one to sign transaction with another keypair.

let secondKeypair: Keypair = new anchor.web3.Keypair(); let context = await startAnchor("",[],[ { address: secondKeypair.publicKey, info: { lamports: 1_000_000_000, data: Buffer.alloc(0), owner: SYSTEM_PROGRAM_ID, executable: false, }, }, ]);beneficiaryProvider = new BankrunProvider(context);beneficiaryProvider.wallet = new NodeWallet(secondKeypair); secondProgram = new Program<Vesting>(IDL as Vesting, beneficiaryProvider);

Using Bankrun for native programs #

You can also use Bankrun fornative programs. The maindifference is that you use start instead of startAnchor to start the Bankrunbank. You can then use the client to interact with the bank.

const context = await start( [{ name: "counter_solana_native", programId: PROGRAM_ID }], [],);const client = context.banksClient;

Instead of using program.instruction().rpc() you can use theawait client.processTransaction(tx).

In the Solana program examples you can find afull native Bankrun example.

Bankrun trouble shooting #

  1. If you encounter an Unknown action 'undefined' error when sending atransaction using Bankrun, you are likely trying to send two identicaltransactions with the same blockhash. Request a new recent blockhash beforesending the second transaction or add some seed or parameter to yourinstructions to make sure they will result in different transaction hashes.

  2. If you encounter Clock handle timeout error you can just restart yourterminal and run the tests again.

Conclusion #

Testing your Solana programs is essential for ensuring they behave as expected.Bankrun offers a lightweight and fast alternative to the local validator, makingyour tests up to 10 times faster. It enables powerful features like customaccount data and time travel, which can significantly enhance your testingcapabilities. Additionally, Jest is a great alternative to Mocha for writingtests and can be easily integrated with Bankrun.

However, it's important to note a few disadvantages of using Bankrun compared tothe local validator:

  1. Environment Representation: Tests run with Bankrun may not fully represent alive or testnet environment.
  2. Code Reusability: Some code used in local validator tests might not bereusable with Bankrun.
  3. Dependency: Using Bankrun and Bankrun Anchor introduces dependencies specificto these tools.

Despite these drawbacks, Bankrun is a valuable tool that can greatly improveyour development workflow.

Speed up Solana program tests with Jest and Bankrun | Solana (2024)
Top Articles
Latest Posts
Article information

Author: Merrill Bechtelar CPA

Last Updated:

Views: 6616

Rating: 5 / 5 (70 voted)

Reviews: 93% of readers found this page helpful

Author information

Name: Merrill Bechtelar CPA

Birthday: 1996-05-19

Address: Apt. 114 873 White Lodge, Libbyfurt, CA 93006

Phone: +5983010455207

Job: Legacy Representative

Hobby: Blacksmithing, Urban exploration, Sudoku, Slacklining, Creative writing, Community, Letterboxing

Introduction: My name is Merrill Bechtelar CPA, I am a clean, agreeable, glorious, magnificent, witty, enchanting, comfortable person who loves writing and wants to share my knowledge and understanding with you.