Learn how to write and run integration tests for your Clarity smart contracts using the Clarinet JS SDK and Vitest.
Integration testing is a crucial step in smart contract development that involves testing how different components of your system work together. The Clarinet JS SDK provides powerful tools for writing and running integration tests, allowing you to simulate complex scenarios and interactions between multiple contracts.
By using integration tests, you can ensure that your smart contracts function correctly as part of a larger system and catch potential issues that might not be apparent in unit tests alone.
Start by creating a new Clarinet project. This command will create a new directory named defi and set up a basic Clarinet project inside it.
Terminal
$
clarinet new stx-defi
$
cd stx-defi
After changing into your project directory, run npm install to install the package dependencies for testing.
Terminal
$
npm install
We are going to use the same defi contract that we used in the unit testing guide, but with some additional functionality - the ability to borrow STX from the contract. If you don't have this project set up already, follow the steps below:
Terminal
$
clarinet contract new defi
Then, inside your defi.clar file, copy and paste the following contract code:
;; Error constants for various failure scenarios.
(define-constanterr-overborrow (erru300))
;; The interest rate for loans, represented as 10% (out of a base of 100).
;; Transfer the STX from sender = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" to recipient = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.defi (ie: contract identifier on the chain!)".
In order to borrow STX from the contract, users must first deposit STX into it. Therefore, we need to write an integration test that simulates the interaction between these two functions.
Inside of your defi.test.ts file, replace the boilerplate code and add the following:
import{ describe, it, expect }from'vitest';
import{ Cl }from'@stacks/transactions';
constaccounts=simnet.getAccounts();
constwallet1=accounts.get('wallet_1')!;
describe('stx-defi', ()=>{
it('borrows 10 and verifies the amount owed to be 10', ()=>{
const{ result }=simnet.callReadOnlyFn('defi','get-amount-owed', [], wallet1);
expect(result).toBeOk(Cl.uint(10));
});
});
In this integration test, we're simulating a scenario where a user deposits STX into the DeFi contract and then borrows against that deposit. Let's walk through the process step by step.
We start by simulating a deposit of 1000 STX from wallet1. To do this, we use the callPublicFn method from the Clarinet JS SDK simnet object, which allows us to call public functions in our smart contract just as we would on the actual blockchain.
After making the deposit, we want to verify that it was successful. We do this by checking the total deposits in the contract using getDataVar.
This handy method lets us peek at the value of data variables defined in your contract.
To learn more about available methods for integration testing, check out the reference page.
To ensure the deposit was recorded correctly, we use a custom matcher, toBeUint. This matcher is specifically designed to check if a value is a Clarity unsigned integer with the exact value we expect.
With the deposit confirmed, we simulate wallet1 borrowing 10 STX. We do this with another call to callPublicFn, this time invoking the borrow function of our contract.
After the borrowing operation, we want to check how much wallet1 owes. We use callReadOnlyFn to call a read-only function named get-amount-owed in our contract.
Finally, we verify the amount owed using another custom matcher, toBeOk(Cl.uint(10)). This matcher is particularly useful because it checks two things at once:
1That our contract returned a successful Clarity response type.
2That the value returned is a Clarity unsigned integer with the exact value we expect (10).
These custom matchers and simnet methods are powerful tools and allow you to simulate complex interactions with your smart contracts and make detailed assertions about the results.