Using Bun
Bun can be used as a test runner and/or package manager. It can be used as a package manager in combination with any other test runner, or as a test runner in combination with any other package manager.
Installing
Section titled “Installing”Installing Bun is as simple as running:
curl -fsSL https://bun.sh/install | bashYou can also check out the official Bun installation documentation for more information.
As a test runner
Section titled “As a test runner”Installation
Section titled “Installation”To get started with Bun as a test runner, install the @types/bun and typescript packages using your preferred package manager:
npm i -D @types/bun typescriptpnpm i -D @types/bun typescriptbun add -d @types/bun typescriptyarn add -D @types/bun typescriptCreate a tsconfig.json file:
{ "compilerOptions": { // enable latest features "lib": ["ESNext"], "target": "ESNext", "module": "ESNext", "moduleDetection": "force", "allowJs": true, // allow importing `.js` from `.ts` "types": ["bun"],
// Bundler mode "moduleResolution": "bundler", "allowImportingTsExtensions": true, "verbatimModuleSyntax": true, "noEmit": true,
// Best practices "strict": true, "skipLibCheck": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true,
// Some stricter flags "useUnknownInCatchVariables": true, "noPropertyAccessFromIndexSignature": true }, "include": ["./tests/**/*.ts", "./global-setup.ts", "./types.d.ts"]}Then, add a test script to your package.json:
{ "scripts": { "test": "tsc && bun test" }}Running tsc is optional, but it is recommended to catch any TypeScript errors before running your tests.
You can also check out the official Bun documentation for TypeScript for more information.
Global test setup
Section titled “Global test setup”The PocketIC server needs to be started before running tests and stopped once they’re finished running. This can be done by creating a global-setup.ts file in your project’s root directory:
import { beforeAll, afterAll } from 'bun:test';import { PocketIcServer } from '@dfinity/pic';
let pic: PocketIcServer | undefined;
beforeAll(async () => { pic = await PocketIcServer.start(); const url = pic.getUrl();
process.env.PIC_URL = url;});
afterAll(async () => { await pic?.stop();});This file can be configured to run with bun test by creating a bunfig.toml file in your project’s root directory:
[test]preload = ["./global-setup.ts"]To improve the type-safety of using process.env.PIC_URL, add a types.d.ts file in your project’s root directory:
declare global { namespace NodeJS { interface ProcessEnv { PIC_URL: string; } }}
export {};Writing tests
Section titled “Writing tests”Bun tests are very similar to tests written with Jest, Jasmine, or Vitest so they will feel very familiar to developers who have used these frameworks before.
The basic skeleton of all PicJS tests written with Bun will look something like this:
import { resolve } from 'node:path';import { afterEach, beforeEach, describe, expect, it } from 'bun:test';import { type Actor, PocketIc } from '@dfinity/pic';
// Import the declarations generated for your canister,// see the Canister declarations guideimport { idlFactory, type _SERVICE } from './declarations/backend.did';
// Define the path to your canister's WASM file.// icp-cli writes it to `.icp/cache/artifacts/<canister name>`.const WASM_PATH = resolve( import.meta.dir, '..', '.icp', 'cache', 'artifacts', 'backend',);
// The `describe` function is used to group tests together// and is completely optional.describe('backend', () => { // Define variables to hold our PocketIC instance // and an actor to interact with our canister. let pic: PocketIc; let actor: Actor<_SERVICE>;
// The `beforeEach` hook runs before each test. // // This can be replaced with a `beforeAll` hook to persist canister // state between tests. beforeEach(async () => { // create a new PocketIC instance pic = await PocketIc.create(process.env.PIC_URL);
// Setup the canister and actor const fixture = await pic.setupCanister<_SERVICE>({ idlFactory, wasm: WASM_PATH, });
// Save the actor for use in tests actor = fixture.actor; });
// The `afterEach` hook runs after each test. // // This should be replaced with an `afterAll` hook if you use // a `beforeAll` hook instead of a `beforeEach` hook. afterEach(async () => { // tear down the PocketIC instance await pic.tearDown(); });
// The `it` function is used to define individual tests it('should greet', async () => { const response = await actor.greet('PicJS');
expect(response).toEqual('Hello, PicJS!'); });});You can also check out the official Bun test runner documentation for more information on writing tests.
As a package manager
Section titled “As a package manager”PicJS downloads the pocket-ic binary from a postinstall script, which Bun blocks by default. Add @dfinity/pic as a trusted dependency in your package.json so the binary is fetched — see allowing the install script for the other package managers:
{ "trustedDependencies": ["@dfinity/pic"]}