Learn how to write and run unit tests for your Clarity smart contracts using the Clarinet JS SDK and Vitest.
Unit testing is the process of testing individual components or functions of smart contracts to ensure they work as expected. The Clarinet JS SDK provides a testing framework that allows you to write these tests using the Vitest testing framework, helping you catch bugs and errors early in the development process.
Start by creating a new project with the Clarinet CLI. The command below will create a project structure inside of defi with the necessary files and folders, including the Clarinet JS SDK already set up for testing.
After changing into your project directory, run npm install to install the package dependencies for testing.
Since the smart contract code is out of scope for this guide, we are going to use a pre-existing contract. First, generate a new file using the clarinet contract new command in order to set up your project with the necessary configuration and test files.
Now, inside your defi.clar file, copy and paste the following contract code:
Run clarinet check to ensure that your smart contract is valid and ready for testing.
You can find the full code for this project in this repo.
This deposit function allows users to deposit STX into the contract, updating their balance inside a deposits map and adding to the total deposits stored in a total-deposits variable. The key tests we want to cover are that the deposit is successful and that the user's balance, as well as the contract's total deposits, are updated correctly.
Inside of your defi.test.ts file, replace the boilerplate code and add the following:
These imports provide the testing framework and utilities we need. We also get the wallet_1 account, which will act as our test user.
Next, define the test suite and the specific test case:
This structure comes from our Vitest integration, and it organizes our tests and describes what we're testing. The describe block groups multiple test cases together, while the it block represents a single test case.
Now, let's simulate a deposit. Inside of the it block, define the amount to deposit and call the deposit function:
This code simulates a deposit by calling the deposit function, using the callPublicFn method from the Clarinet JS SDK, in our contract with a specified amount, just as a user would in the real world.
After making the deposit, create an assertion to verify that the call itself was successful and returns an ok response type with the value true:
Run npm run test to confirm that this test passes.
Let's go over some of the code in this assertion:
expect is a function from Vitest that makes an assertion about the value we expect to get back from the deposit function.
But how do we test against Clarity types and values? This is where the Cl and toBeOk helpers come in.
toBeOk is a custom matcher function built into Vitest that checks if the result of the deposit call is an Ok response, which is a Clarity type. This is important because it confirms that the deposit transaction was processed successfully.
Cl helper is from the @stacks/transactions package and is used to create Clarity values in JavaScript. In this case, it's used to create a Clarity boolean with the value of true.
To see more custom matcher examples, check out the reference page.
Once we can confirm that the deposit was successful, write a test to verify that the contract's total deposits have been updated correctly.
Run npm run test again to confirm that this test also passes.
This check ensures that the contract accepted our deposit without any issues.
Lastly, verify that the user's balance has been updated correctly:
We call the get-balance-by-sender function and check if it matches the amount we just deposited.
By following these steps, our test comprehensively verifies that the deposit function works as intended, updating individual balances and total deposits accurately.