To learn more about writing unit tests using Clarigen, check out the quick start guide.
Jump to a section:
Importing Simnet Contracts and Accounts
A "simnet" is an environment where you can interact with your contracts without running a full Stacks chain. When using Clarinet and Clarinet SDK to write tests, you're using a simnet.
Clarigen automatically includes your simnet contracts and accounts.
To get your simnet contracts and accounts, you can import them and use projectFactory to configure your contracts for simnet:
import { project, accounts } from './clarigen-types';
import { projectFactory } from '@clarigen/core';
const contracts = projectFactory(project, 'simnet');Calling read-only functions
Clarigen provides helper functions to make strongly typed read-only function calls.
In these examples, assume a contract with a validate-number and get-rate function like so:
;; Return ok if `num` greater than or equal to 100
(define-read-only (validate-number (num uint))
(if (>= num u100) (ok num) (err "Number must be greater than 100"))
)
(define-read-only (get-rate)
(begin
(print { "rate": u555 })
(ok u555)
)
)ro ("read-only")
If you want to receive the full response, including events, you can use ro:
const rateReceipt = ro(contract.getRate());
expect(rateReceipt.value).toEqual(555n);
// rateReceipt.events is the list of events emitted by the contract call
expect(rateReceipt.events.length).toEqual(1n);
const validation = ro(contract.getRate(100));
//validation.value.value has the type `bigint | string`
expect(validation.value.value).toEqual(100n);
expect(validation.value.isOk).toBe(true);The return type of ro is:
{
value: T;
result: ClarityValue;
events: CoreNodeEvent[];
}rov ("read-only value")
If you only care about the return value of the contract call, you can use rov:
const rate = rov(contract.getRate());
expect(rate).toEqual(555n);
const validationResponse = rov(contract.validateNumber(100));
// `validationResponse.value` has the type `bigint | string`
expect(validationResponse.value).toEqual(100n);rovOk and rovErr
For functions that return a response, you can use rovOk and rovErr to assert that the response is either ok or err and return the inner value:
const okResult = rovOk(contract.validateNumber(101));
// okResult is 101n
const errResult = rovErr(contract.validateNumber(99));
// errResult is "Number must be greater than 100"
rovOk(contract.validateNumber(99)); // throws an error
rovErr(contract.validateNumber(101)); // throws an errorCalling public functions
When making transactions, you provide two arguments:
- The function call payload
- The sender (
string) address
For these examples, assume an increment function that looks like this:
(define-data-var counter uint u0)
(define-public (increment (amount uint))
(if (< 5 amount)
(begin
(var-set counter (+ (var-get counter) amount))
(ok amount)
)
(err false)
)
)Transaction receipt type
When calling public functions, the return type is:
{
value: T;
events: CoreNodeEvent[];
result: ClarityValue;
}tx
const receipt = tx(contract.increment(1), alice);
// result.events is a list of events emitted
if (receipt.value.isOk) {
// receipt.value.value is the `ok` type
} else {
// receipt.value.value is the `err` type
}txOk and txErr
If you want to automatically throw an error and get the inner value of the ok or err type, you can use txOk and txErr:
const okResult = txOk(contract.increment(3), alice);
expect(okResult).toEqual(3n);
const errResult = txErr(contract.increment(10), alice);
expect(errResult).toEqual(false);
txOk(contract.increment(10), alice); // throws an error
txErr(contract.increment(3), alice); // throws an errorFiltering events
If you want to get specific events from a receipt, you can use filterEvents from @clarigen/test.
import { CoreNodeEventType } from '@clarigen/core';
import { filterEvents } from '@clarigen/test';
const printEvents = filterEvents(receipt.events, CoreNodeEventType.ContractEvent);
// `printEvents` has the type `SmartContractEvent[]`
const tokenTransfers = filterEvents(receipt.events, CoreNodeEventType.FtTransferEvent);
// `tokenTransfers` has the type `FtTransferEvent[]`You can combine this with cvToValue from @clarigen/core to easily verify print events:
import { CoreNodeEventType, cvToValue } from '@clarigen/core';
import { filterEvents } from '@clarigen/test';
const print = printEvents[0];
const printData = cvToValue(print.data.value);
expect(printData).toEqual({
topic: 'mint',
amount: 100n,
});