ic-testkit
ic-testkit is a small wrapper and helper layer around pocket-ic, the local Internet Computer testing runtime this crate stands on. It does not replace pocket-ic; it adds reusable Rust test-harness conveniences on top of it.
Use pocket-ic directly when you want the underlying simulator/runtime API. Use ic-testkit when you want typed Candid calls, install helpers, serialized PocketIC startup, cached baselines, deterministic fake principals, wasm artifact utilities, and compact benchmark reporting.
ic-testkit is intentionally application-neutral. Bring your own init payloads, method names, canister graph, readiness checks, labels, thresholds, and CI policy.
Install
[]
= "0.1.0"
For canister-side benchmark markers:
[]
= { = "0.1.0", = ["canister"] }
[!WARNING] Do not use - some of this may be hallucinations, our best agents are currently auditing the code.
Quick Start
Use PicSerialGuard when a test owns a PocketIC instance. It serializes PocketIC usage across processes, which helps avoid shared server/resource exhaustion in larger test runs.
use ;
Calling Canisters
Pic wraps common update/query calls with Candid encoding and decoding. Rejections include the canister id and method name.
use ;
Use the _as variants when caller identity matters:
let caller = principal;
let ledger_id = principal;
let balance: u128 = pic
.query_call_as
.unwrap;
Installing Wasm
For one-off tests, install a prebuilt wasm into a fresh PocketIC instance:
use ;
For an existing Pic, use the lower-level helper:
// `pic` is an ic_testkit::pic::Pic from your test setup.
// `wasm` is a Vec<u8> containing the compiled canister wasm.
let canister_id = pic.create_and_install_with_args;
If PocketIC reports install-code rate limiting, retry while advancing PocketIC time between attempts:
use Duration;
// `pic` is an ic_testkit::pic::Pic from your test setup.
// `wasm` is a Vec<u8> containing the compiled canister wasm.
let result = pic.retry_install_code_ok;
Artifact Helpers
Build wasm packages into a dedicated target directory:
use artifacts;
let workspace = workspace_root_for;
let target = test_target_dir;
build_wasm_canisters;
assert!;
Check generated .icp artifacts against watched inputs:
use artifacts;
let workspace = workspace_root_for;
let ready = icp_artifact_ready_for_build;
Benchmark Reports
ic_testkit::benchmark turns compact canister log markers into parsed events, paired spans, aggregate rows, comparison rows, CSV files, and a Markdown summary.
The default marker prefix is ICTK. The compact marker shape is:
ICTK|<label>:start|<instructions>|<heap_bytes>|<memory_bytes>|<total_allocation>
ICTK|<label>:end|<instructions>|<heap_bytes>|<memory_bytes>|<total_allocation>
Example:
ICTK|app/myfunc/something:start|100|200|300|400
ICTK|app/myfunc/something:end|150|260|390|430
Parse, pair, and aggregate captured logs:
use ;
let logs = "\
ICTK|app/myfunc/something:start|100|200|300|400
ICTK|app/myfunc/something:end|150|260|390|430
";
let parsed = parse_benchmark_events;
let spans = pair_benchmark_spans;
let aggregates = aggregate_benchmark_spans;
assert_eq!;
If the harness captures stdout and stderr separately, use parse_benchmark_events_from_captured_output(stdout, stderr, config) to preserve the source stream for each marker.
The report writer emits:
raw-events.csvspans.csvsuite-aggregates.csvall-aggregates.csvmalformed-markers.csvunpaired-markers.csvinvalid-spans.csvbench-summary.mdmetadata.json
The Markdown summary is optimized for quick cost review. Rows include the span label, run count, average instruction cost in billions, memory deltas in human units, and optional percentage changes against the previous run, for example 0.2342B (+34%) and +4.0 MB (-23%).
Run helpers create directories such as reports/runs/2026-05-24T162600Z-a1b2c3d-0001/ and discover the latest compatible previous run:
use ;
let run = next_benchmark_run_directory?;
let previous = find_latest_previous_run?;
# Ok::
Canister-Side Markers
Enable the canister feature in canister code and call Performance::measure around the region under measurement:
use Performance;
measure;
// code under measurement
measure;
The helper prints the compact ICTK|... line with the IC CDK call-context instruction counter, Wasm linear memory size, stable memory size, and a total_allocation slot. total_allocation is currently emitted as 0 because the IC CDK does not expose Rust allocator total allocation.
This repository includes a real PocketIC fixture canister under canisters/test/perf_probe so the marker path can be tested end to end:
Cached Baselines
For expensive multi-canister setup, CachedPicBaseline can snapshot canisters once and restore them between tests. If the cached PocketIC instance has died, restore_or_rebuild_cached_pic_baseline rebuilds instead of reusing a broken instance.
Use cached baselines when setup time dominates the test and the fixture can be restored from PocketIC snapshots. Keep application-specific topology and readiness logic in your own test harness.
Deterministic Test Identities
Fake gives stable principals and account-like values from numeric seeds:
use Fake;
let alice = principal;
let bob = principal;
let account = account;
assert_ne!;
assert_eq!;
What This Adds Over pocket-ic
Pic, a narrow wrapper for common PocketIC operations- typed startup errors for common PocketIC launch failures
PicSerialGuardfor cross-process PocketIC serialization- Candid query/update helpers with contextual errors
- generic wasm install helpers and install-code retry helpers
- canister status/log diagnostics
- standalone prebuilt-wasm fixtures
- cached snapshot baselines
- deterministic fake principals and accounts
- wasm path/build/readiness helpers
- watched-input freshness checks for generated
.icpartifacts - compact benchmark marker parsing, pairing, aggregation, comparison, and report writing
- optional canister-side
Performance::measuremarker emission - an in-repo PocketIC fixture canister for testing the benchmark marker path
Boundaries
This crate does not define application init payloads, endpoint names, role models, readiness polling, canister graph topology, benchmark labels, threshold policy, CI failure policy, or broad self-test orchestration. Those belong in the application or framework that owns the canisters being tested.
Toolchains
- MSRV: Rust 1.88
- internal build/test lane: Rust 1.95
Local Checks