Skip to main content

Testing

The layers

LayerRunsNeeds
Workspace unit suitesnpm test; an explicit root sequence of Bun/workspace testsNothing external
App Motoko testsApp test:motoko runners compile and execute fixtures with neutron-motoko-wasmNothing external
Security fixturesnpm run security:check applies the source policyNothing external
Typechecknpm run typecheck checks the referenced TypeScript project graphNothing external
Browser / E2EPlaywright specsChromium; local-runtime specs also need a provisioned Neutron

Start with the root baseline:

npm test
npm run typecheck
npm run security:check

Browser tests are separate. Requirements vary by spec: install and workspace flows need a real canister and gateway, while compiler-worker tests exercise the browser compiler directly.

What the repository covers

Fast checks cover manifest validation, package encoding and bounded decoding, path rewriting and hash verification, actor assembly output, the migration planner, capability plan projection and fingerprints, config and session handling, the compile cache, PocketIC supervisor attachment, fixtures, per-node reinstall progress, and the message-bus policy.

Security fixtures run the same ordered policy used by packaging and compilation over known-good and known-bad Motoko. Unexpected parser failures fail the suite rather than passing silently.

Browser coverage includes logged-out and local-authenticated startup, optional local Internet Identity, package review/install and updates, workspaces, tiles, trays, Settings, typed calls, malformed requests, selected cross-app flows, and the Motoko compiler worker. The :fresh commands below first perform a destructive whole-canister local reinstall; commands without :fresh reuse the selected config's recorded session.

What your app should test

Migrations — the one that will bite you

A user may install your v3 over v1 or over v2. The compiler verifies types and stable signatures. Only your tests verify that the data comes out meaning what it meant before.

Test every advertised start version:

StartAssert
Clean install at targetinit() produces the intended default state
v1 → targetEvery field carries over with correct semantics
v2 → targetSame
Skipped versionsThe selected path composes correctly

Also verify that your neutron.lock.json is unchanged for schemas you did not intend to touch. A lock diff on a released schema means you edited history.

Manifest and packaging

That packing succeeds; that the resulting archive contains the expected manifest, web assets, and modules; and — worth asserting explicitly — that your declared capabilities are exactly what you intended. The manifest is a declaration of maximum authority, so an accidental broad scope is a real defect.

Backend logic

Ordinary unit tests over your Init class. Because it is a plain module, you can construct it directly with a test environment record — no actor, no replica.

That is a genuine advantage of the module shape: your backend is testable as ordinary Motoko.

Frontend behaviour

Especially the state-invalidation path. Verify that a view fetches a full snapshot on mount and on reconnect, that it compares revisions monotonically, and that a late async response cannot overwrite a newer applied revision. That class of bug only shows up when a tile is backgrounded and resumed.

Dependencies

If you export functions to other apps, test that a consumer's expected signature still type-checks after your change. Breaking an exported signature means a new function name, not an edited one.

Local environment

The provisioner supervises one PocketIC process, one persisted instance, its fixtures, and a fixed browser gateway. Chain-integration work additionally gets persistent loopback-only Bitcoin regtest and Anvil processes with pinned real ckBTC and ckETH canisters funded through their native deposit paths.

For the default local.ndeploy.json deployment:

# terminal 1
npm run local:start

# terminal 2
npm run local:deploy # destructive whole-canister reinstall
npm run local:status

Local browser and E2E helpers read the running canister, gateway, and PocketIC attachment from the selected config's session, and derive the deterministic developer identity from that config's seed. You do not hardcode ids.

:::note Raise the browser stack size The in-browser Motoko compiler can overflow a default Chromium stack while compiling kernel plus apps. Local test environments set a larger JS stack explicitly; if install tests fail with a stack overflow, that is the cause. :::

Browser commands

npm run test:e2e
npm run test:e2e:local
npm run test:e2e:local:ii
npm run test:e2e:local:fresh
npm run test:e2e:kitchensink
npm run test:e2e:kitchensink:fresh
npm run test:e2e:package-updates
npm run test:e2e:package-updates:fresh

On NixOS, run these from nix develop so Playwright receives the repository's Chromium executable and --js-flags=--stack-size=16384 launch argument.