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.

counter.clar
counter.test.ts
.gitignore
Clarinet.toml
package.json
tsconfig.json
vitest.config.js
counter.test.ts
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.

counter.test.ts
import { describe, expect, it } from "vitest";
import { Cl } from "@stacks/transactions";

const accounts = simnet.getAccounts();
const wallet = accounts.get("wallet_1")!;
Note

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:

  1. Define the group of tests you want to run using the describe block.
  2. Define the test you want to run using the it block.
  3. Call 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.

counter.test.ts
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.

counter.test.ts
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.

Custom matchers

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.

package.json
"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.

package.json
"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:

counter.clar
counter.test.ts
.gitignore
Clarinet.toml
costs-reports.json
lcov.info
package.json
tsconfig.json
vitest.config.js

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:

costs-reports.json
  {
    "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.

Terminal
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.

Terminal
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.


Next steps