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.
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});
});
txn.send();
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 });
zkAppInstance.init();
});
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.