Quickstart
Learn how to test a simple counter contract using the Clarinet JS SDK.
In this quickstart guide, you will initialize a simulated development network for testing a smart contract using the Clarinet JS SDK. You'll learn how to initialize your project, interact with a smart contract, call its functions, and test the results.
Check out the methods page for the Clarinet JS SDK to learn about the different ways that you can interact with simnet.
Importing dependencies
Before we dive in, navigate to your project and import the Cl
helper from the @stacks/transactions
package in your test file.
import { describe, expect, it } from "vitest";import { Cl } from "@stacks/transactions";
The Cl
namespace simplifies the process of creating and handling Clarity values. This functionality is particularly useful in testing environments where developers need to simulate contract interactions accurately.
Retrieve an account from the simnet
For most test cases, you'll want to retrieve an account from the network in order to interact with your smart contracts. The following code uses the getAccounts
method to achieve this.
import { describe, expect, it } from "vitest";import { Cl } from "@stacks/transactions";const accounts = simnet.getAccounts();const wallet = accounts.get("wallet_1")!;
The --stacks
flag is required and allows you to specify the network to scan. Other options include --bitcoin
.
Write your first test
The process for writing tests for smart contracts with the Clarinet JS SDK follows a structured approach similar to testing in JavaScript. Here's a breakdown of the steps involved:
- 1Define the group of tests you want to run using the
describe
block. - 2Define the test you want to run using the
it
block. - 3Call the function and assert the result using the
expect
function.
Now that you understand the key components for writing tests, start by writing a test that ensures the counter
contract has been deployed.
describe("counter contract", () => {it("ensures the contract is deployed", () => {const contractSource = simnet.getContractSource("counter");expect(contractSource).toBeDefined();});});
Test the count-up function
Now it's time to call a public function and assert the result. The count-up
function is expected to increment the count of the user's principal by 1. Write a test to ensure this behaves as expected.
describe("counter contract", () => {it("increments the count of the user's principal by 1", () => {const countUpCall = simnet.callPublicFn("counter", "count-up", [], wallet);expect(countUpCall.result).toBeOk(Cl.bool(true));const getCountCall = simnet.callReadOnlyFn("counter","get-count",[Cl.principal(wallet)],wallet);expect(getCountCall.result).toBeUint(1);});});
Above, the count-up
function is called, and the result is asserted to return an (ok true)
. Then, the count for the user's principal is retrieved and asserted to be u1
.
The toBeOk
and toBeUint
methods are used to ensure the count-up
function returns the proper Clarity values. For more details, check out the custom matchers page.
Run your tests
Every generated project comes with a package.json
file that contains the necessary dependencies and scripts to run your tests.
"scripts": {"test": "vitest run","test:report": "vitest run -- --coverage --costs","test:watch": "chokidar \"tests/**/*.ts\" \"contracts/**/*.clar\" -c \"npm run test:report\""}
You can now run your tests, with your preferred package manager, by executing the following command:
$npm run test
Run your tests with code coverage and cost analysis
Clarinet can automatically also produce a code coverage report and cost analysis on the test you ran.
"scripts": {"test": "vitest run","test:report": "vitest run -- --coverage --costs","test:watch": "chokidar \"tests/**/*.ts\" \"contracts/**/*.clar\" -c \"npm run test:report\""}
By running the npm run test:report
command in your terminal, an lcov.info
and costs-reports.json
file will appear in your root folder:
The costs-reports.json
file will output the cost analysis for every function ran in your tests. Since the count-up
function was used in our test, this is how the cost analysis output would look like for that function:
{"test_name": "tests/counter.test.ts__counter contract__increments the count of the user's principal by 1","contract_id": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.counter","method": "count-up","args": [],"cost_result": {"total": {"write_length": 41,"write_count": 1,"read_length": 245,"read_count": 5,"runtime": 4752},"limit": {"write_length": 15000000,"write_count": 15000,"read_length": 100000000,"read_count": 15000,"runtime": 5000000000},"memory": 40,"memory_limit": 100000000}}
To access and read the lcov.info
file in a human-readable format, we can first install the lcov
package that helps generate a graphical front-end for coverage testing tools.
$brew install lcov
Then we'll run the genhtml
command below with the following options. This command and its options will generate the actual html file, add in additional branch coverage, and will automatically store the files in a new folder called coverage
.
$genhtml lcov.info --branch-coverage -o coverage
You can then navigate into the new coverage
folder and run the command open index.html
to view your coverage report. In addition, a summary of the coverage report will be outputted in the terminal.