@tonkite/jest-tolk
v1.0.2
Published
<p align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/tonkite/tonkite/main/assets/logo-dark.svg"> <img alt="tonkite logo" src="https://raw.githubusercontent.com/tonkite/tonkite/main/a
Readme
Jest Runner for Tolk
A Jest runner that enables you to write and execute unit tests for Tolk code using familiar testing patterns.
Example
Consider a simple Tolk function in sum.tolk:
fun calculateSum(a: int, b: int) {
return a + b;
}You can write unit tests for this function using Tolk:
import "../node_modules/@tonkite/jest-tolk/testing.tolk"
import "sum.tolk"
// @scope sum()
get test_returns_sum_of_numbers() {
val a: int = 4;
val b: int = 7;
assert(calculateSum(a, b) == a + b) throw 100;
}
fun cast<T, X>(value: X): T asm "NOP";
// @scope sum()
get test_fails_if_value_is_not_int() {
test.expectExitCode(7); // type check error
val a: int = cast<int>(null);
val b: int = 7;
calculateSum(a, b);
}Test result:
Installation
Install
@tonkite/jest-tolk:pnpm add -D @tonkite/jest-tolkAdd runner configuration to
jest.config.ts:import type { Config } from 'jest'; const config: Config = { projects: [ { displayName: 'test', preset: 'ts-jest', testEnvironment: 'node', testPathIgnorePatterns: ['/node_modules/', '/dist/'], }, { displayName: 'tolk', moduleFileExtensions: ['tolk'], testMatch: ['**/*[._]test.tolk'], runner: '@tonkite/jest-tolk', }, ], }; export default config;
Fuzzing
The Runner comes with native fuzz-testing support. For every fuzz test (starting from testFuzz_) it:
Generates random values for each argument.
Executes the test repeatedly (default 100 runs).
Stops on the first failure and prints the failing inputs.
Adjust the iteration count with the @runs annotation.
Fuzz-able types
- Signed and unsigned integers of any width —
int,int8,uint231, etc. (boolis treated as a one-bit unsigned int). address(includingaddr_std,addr_externandaddr_none)
Example:
fun div(x: int, y: int): int
asm "DIV";
/** @runs 1000 */
get testFuzz_div(x: int8, y: uint16) {
if (y == 0) {
test.expectExitCode(4); // division by zero
}
Assert.equal(x / y, div(x, y));
}Annotations
The runner allows you to configure the behavior of tests using special annotations in comments.
Example:
/**
* @scope examples
*/
get test_fail_with_exit_code_500() {
test.expectExitCode(500);
throw 500;
}Supported Annotations:
| Annotation | Example | Description |
|:------------------------|:-------------------------|:---------------------------------------------------------------------------------------|
| @scope [scope] | // @scope Pool::onSwap | Specifies the scope of a test (useful for test grouping). |
| @skip | // @skip | Marks a test to be skipped. |
| @todo | // @todo | Marks a test to be done later. |
| @gasLimit [gas limit] | // @gasLimit 50000 | Sets a gas limit for a test. Default: 10000. |
| @runs [runs] | // @runs 1000 | Sets a number of iterations for fuzzing. Default: 100. |
| @no-main | // @no-main | Disables adding an entrypoint fun main() {} to avoid collision with an existing one. |
Test Helpers & Assertions
Test Helpers
test.expectExitCode(exitCode: int)Signals that the current test must terminate withexitCode. The runner fails the test if any other exit code (or none) is produced.test.assume(condition: bool)Skips the remainder of the test whenconditionisfalse. Useful in fuzz/property tests to ignore meaningless input.test.getC7(): tupleReturns the current value of control register C7.test.setC7(c7: tuple)Replaces the entire C7 register withc7. Use with care.test.setConfigParam<T>(value: T, index: int)Internal helper that overwrites one slot inC7(valueis generic).test.setTime(now: int)Overridesblockchain.now()for deterministic testing (config-param #3).test.setBlockLogicalTime(blockLT: int)Overrides the block logical-time stamp (config-param #4).test.setLogicalTime(lt: int)Overrides the transaction logical-time stamp (config-param #5).test.setOriginalBalance(balance: coins, extraCurrencies: dict? = null)Fakes the contract’s starting balance (config-param #7).
Assertions
Assert.equal(actual: int, expected: int, msg: slice = "")Assert.notEqual(actual: int, expected: int, msg: slice = "")Assert.equalAddress(actual: address, expected: address, msg: slice = "")Assert.isNull<T>(value: T, msg: slice = "")Assert.notNull<T>(value: T, msg: slice = "")Assert.isTrue(flag: bool, msg: slice = "")Assert.isFalse(flag: bool, msg: slice = "")Assert.greaterThan(a: int, b: int, msg: slice = "")Assert.greaterThanOrEqual(a: int, b: int, msg: slice = "")Assert.lessThan(a: int, b: int, msg: slice = "")Assert.lessThanOrEqual(a: int, b: int, msg: slice = "")Assert.size(t: tuple, expected: int, msg: slice = "")Assert.isInternalAddress(addr: address, msg: slice = "")Assert.isExternalAddress(addr: address, msg: slice = "")Assert.isNoneAddress(addr: address, msg: slice = "")Assert.consumesLessThan(fn: () -> void, gasLimit: int, msg: slice = "")Assert.fail(message: slice, code: slice = "")
Example Usage
get sample_test() {
;; Freeze time and set deterministic logical-times
test.setTime(1_726_689_600); ;; 2025-06-23 UTC
test.setBlockLogicalTime(1_234_567);
test.setLogicalTime(1_234_568);
;; Basic assertions
Assert.isTrue(2 + 2 == 4);
Assert.equal(0xdead, 0xdead);
;; Expect exit-code 13 from the next call
test.expectExitCode(13);
myContract.someDangerousCall();
}Happy testing 🚀
