Skip to content

Running tests

By default, the PocketIC server binary downloaded by the @dfinity/pic install script is used, which requires allowing the install script. A different binary can be provided using the binPath option, for example:

const picServer = await PocketIcServer.start({
binPath: '/path/to/pocket-ic',
});

Alternatively, the POCKET_IC_BIN environment variable can be set to the path of the binary, for example:

Terminal window
POCKET_IC_BIN=/path/to/pocket-ic npm test

The binPath option takes precedence over the POCKET_IC_BIN environment variable.

Logs for canisters can be configured when running the PocketIC server using the showCanisterLogs option, for example:

const picServer = await PocketIcServer.start({
showCanisterLogs: true,
});

To assert on a canister’s logs in a test, fetch its log records with fetchCanisterLogs. By default, only the canister’s controllers can read its logs; other principals need the canister’s log visibility to allow them:

const pic = await PocketIc.create(picServer.getUrl());
// set up a canister controlled by `controller`...
const logs = await pic.fetchCanisterLogs({
canisterId,
sender: controller.getPrincipal(),
});
const messages = logs.map(log => new TextDecoder().decode(log.content));

Records can be narrowed to an index or timestamp range with filter, for example filter: { type: 'byIdx', start: 0n, end: 10n }.

Logs for the PocketIC server can be configured by setting the POCKET_IC_LOG_DIR and POCKET_IC_LOG_DIR_LEVELS environment variables.

The POCKET_IC_LOG_DIR variable specifies the directory where the logs will be stored. It accepts any valid relative, or absolute directory path.

The POCKET_IC_LOG_DIR_LEVELS variable specifies the log levels. It accepts any of the following string values: trace, debug, info, warn, or error.

For example:

Terminal window
POCKET_IC_LOG_DIR=./logs POCKET_IC_LOG_DIR_LEVELS=trace npm test

It’s also possible to set individual log levels for different modules, for examples:

Terminal window
POCKET_IC_LOG_DIR=./logs POCKET_IC_LOG_DIR_LEVELS=pocket_ic_server=trace,tower_http=info,axum::rejection=info npm test

Logs for the IC runtime can be configured when running the PocketIC server using the showRuntimeLogs option, for example:

const picServer = await PocketIcServer.start({
showRuntimeLogs: true,
});

These include the logs of each instance’s replica, which only logs warnings and errors by default. To see more, set its log level with the logLevel option when creating the instance, one of critical, error, warn, info, debug or trace:

const pic = await PocketIc.create(picServer.getUrl(), {
logLevel: 'debug',
});

While a call waits at an await, for example for an inter-canister call, the canister can execute other calls. If the first call checked some state before the await and updates it after, another call can act on the same state in between, which can cause reentrancy bugs.

To reproduce this, submit the calls with a deferred actor before executing any of them. Calling a method of a deferred actor submits the call and returns a function that executes it. Calls submitted together are executed together, so they interleave at their awaits. The Reentrancy example uses this to show a withdrawal that pays out twice, and how to fix it. For calls without generated declarations, pic.submitCall and pic.awaitCall do the same with a Candid-encoded argument.

makeLive makes the instance execute rounds on its own, in real time, and starts an HTTP gateway, so agents can call canisters as they would on a real network:

const gatewayPort = await pic.makeLive();
const agent = await HttpAgent.create({
host: `http://localhost:${gatewayPort}`,
shouldFetchRootKey: true,
});
// make calls with the agent...
await pic.stopLive();

The gateway can be configured with the httpGateway option, for example pic.makeLive({ httpGateway: { port: 8080 } }).

To create an instance that is live from the start, set the autoProgress option, for example PocketIc.create(url, { autoProgress: {} }). Its time follows the real time right away, and makeLive starts the HTTP gateway for it.

To test how a client handles slower calls, for example its loading states or timeouts, set a minimum delay between rounds with artificialDelayMs, for example pic.makeLive({ artificialDelayMs: 500 }). This delays calls that wait for live mode to execute them: calls made through the HTTP gateway, and calls submitted with pic.submitCall whose result is checked with pic.ingressStatus. Calls made with an actor, a deferred actor, pic.updateCall or pic.awaitCall execute their rounds themselves and are not delayed.