Testing
The layers
| Layer | Runs | Needs |
|---|---|---|
| Workspace unit suites | npm test; an explicit root sequence of Bun/workspace tests | Nothing external |
| App Motoko tests | App test:motoko runners compile and execute fixtures with neutron-motoko-wasm | Nothing external |
| Security fixtures | npm run security:check applies the source policy | Nothing external |
| Typecheck | npm run typecheck checks the referenced TypeScript project graph | Nothing external |
| Browser / E2E | Playwright specs | Chromium; 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:
| Start | Assert |
|---|---|
| Clean install at target | init() produces the intended default state |
| v1 → target | Every field carries over with correct semantics |
| v2 → target | Same |
| Skipped versions | The 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.