Skip to main content
info

Please note that zkApp programmability is not yet available on Mina Mainnet, but zkApps can now be deployed to Berkeley Testnet.

How to Test a zkApp

Writing automated tests for your smart contract is of crucial importance. The Jest testing framework is included in all projects created by the Mina zkApp CLI via zk project <name> and zk example <name. We recommend Jest, but any testing framework can be used.

Running tests

To run all test files within your project, run npm run test or npm run testw (for watch mode) from your project’s root directory.

To generate a test coverage report for your project, run npm run coverage. Coverage reports show the % of your code that is tested. The result will be output in your terminal. This can be helpful to ensure your code is well tested.

Writing tests

Creating tests for your smart contract is easy using the Mina zkApp CLI. To scaffold a TypeScript file with a corresponding test file, simply run the command zk file foo. This will generate two files, named foo.ts and foo.test.ts. foo.test.ts is a great place to start writing all your smart contract test code. To write good unit tests, it's vital that you concretely understand the functionality your smart contract provides. An example is shown below of a basic test written in Jest:

describe('foo.test.ts', () => {
describe('test()', () => {
// your test here
});
});

Because we are using Jest, it's helpful to break down all functionality of your smart contract into describe blocks. Mapping out your unit tests like this is also a good way of providing documentation to other developers reading your smart contract. An excellent place to start testing your smart contract is in the areas your smart contract modifies its state. Make sure to verify that your state updates in the way you expect.

For examples of existing tests, we recommend creating a template example using the Mina zkApp CLI via zk project <name> and examining the test file there. You will see a basic example of a few tests that deploy and update the state on a smart contract using Jest.

Creating a local blockchain

It's helpful to run your smart contract locally on a mock blockchain to quickly test it. The Mina.LocalBlockchain() method specifies a mock Mina ledger of accounts that runs locally on your machine. It contains logic for updating the ledger that can be used to test your smart contract.

tip

Mina.LocalBlockchain() takes an optional parameter { proofsEnabled: true } with default value true, which lets you specify whether or not you want to generate and verify proofs. If you set proofsEnabled: false, the local instance will behave exactly the same as before, but it won't generate proofs or verify them. This can be very useful when you want to run tests in the CI or quickly debug your smart contract without having to wait for proofs to be generated.

You can also programmatically enable and disable the proofsEnabled flag within your test flow by calling Local.setProofsEnabled(x: boolean).

let Local = Mina.LocalBlockchain();
Mina.setActiveInstance(Local);

Deploying a contract locally

The mock Mina local blockchain contains test accounts that can be used to deploy smart contracts and pay transaction fees.

First, access a test account provided by the local blockchain.

// Local.testAccounts is an array of 10 test accounts that have been pre-filled with Mina
let feePayer = Local.testAccounts[0].privateKey;

Next, generate a zkApp account and a new instance of the smart contract to deploy locally for testing.

// zkapp account
let zkAppPrivateKey = PrivateKey.random();
let zkAppAddress = zkAppPrivateKey.toPublicKey();
let zkAppInstance = new Add(zkAppAddress);

Then, use the test account and zkApp keys to construct a transaction to pay account creation fees and deploy your smart contract. This transaction is sent to the local blockchain.

let txn = await Mina.transaction(feePayer, () => {
Party.fundNewAccount(feePayer);
zkAppInstance.deploy({ zkappKey: zkAppPrivateKey });
});

const txPromise = await txn.send();
/*
`txn.send()` returns a promise with two closures - `.wait()` and `.hash()`
`.hash()` returns the transaction hash, as the name might indicate
`.wait()` automatically resolves once the transaction has been included in a block. this is redundant for the LocalBlockchain, but very helpful for live testnets
*/

await txPromise.wait();

Writing integration tests

Jest can be used to write both unit and integration tests. A well written integration test tests the flow of your smart contracts expected behavior, and verifies correctness of state updates. After you test the expected behavior, it's benifical to verify the preconditions of your methods and test edge cases of your smart contract. Below is an example of a basic integration test script.

describe('Add smart contract integration test', () => {
let feePayer: PrivateKey,
zkAppAddress: PublicKey,
zkAppPrivateKey: PrivateKey,
zkAppInstance: Add,
currentState: Field,
txn;

beforeAll(async () => {
await isReady;
// setup local blockchain
let Local = Mina.LocalBlockchain();
Mina.setActiveInstance(Local);

// Local.testAccounts is an array of 10 test accounts that have been pre-filled with Mina
feePayer = Local.testAccounts[0].privateKey;

// zkapp account
zkAppPrivateKey = PrivateKey.random();
zkAppAddress = zkAppPrivateKey.toPublicKey();
zkAppInstance = new Add(zkAppAddress);

// deploy zkapp
txn = await Mina.transaction(feePayer, () => {
Party.fundNewAccount(feePayer);
zkAppInstance.deploy({ zkappKey: zkAppPrivateKey });
});
await txn.send();
});

afterAll(async () => {
setTimeout(shutdown, 0);
});

it('sets intitial state of num to 1', async () => {
currentState = zkAppInstance.num.get();
expect(currentState).toEqual(Field(1));
});

it('correctly updates num from intial state to 3', async () => {
txn = await Mina.transaction(feePayer, () => {
zkAppInstance.update();
zkAppInstance.sign(zkAppPrivateKey);
});
txn.send();

currentState = zkAppInstance.num.get();
expect(currentState).toEqual(Field(3));
});
});

Learn more

Please see the Jest docs for further information on how to use Jest.

We will be adding blockchain-specific testing functionality in the future. Keep an eye on this section for updates.

Next Steps

Now that you've learn how to test a smart contract, you can now learn how to deploy a zkApp.