Testing Your Canister With Vitest
• 5 minutes readHave you gotten started building on the Internet Computer and are interested in writing some automated tests for your code? This is a quick guide on how to get started, using the Hello World starter app.
An example project with all the code used here is available at https://github.com/krpeacock/sample-canister-e2e.
Setting Up
First, start with a dfx new hello
project. Remove the hello_assets
folder and then remove the canister config in dfx.json. Once you’ve cleaned up the boilerplate, your config should look like this:
// dfx.json
Next, install vitest
and isomorphic-fetch
. Note: You can use Jest instead, but you’ll need to do a little more setup.
Add "test": "vitest"
to your package.json
scripts.
Actor setup
Create a folder for your tests. I place mine in <project-root>/src/e2e
. Inside of e2e
, create a utility to create your agent using the generated declarations, named actor.js
. Note: I’m mixing use of JS and TS files here because there are a couple annoying TS warnings with these imports that aren’t worth fixing.
// actor.js
;
;
;
;
;
;
;
Since we are testing locally, we always will fetch the root key.
This setup file handles reading canister IDs from their JSON, importing IDL from the declarations, creating a default actor and configuring it with a fetch polyfill (not necessary in Node 16+) and local host.
Writing tests
Now, create a file for your tests. hello.test.ts
. First, we’ll set up the basics, importing our testing methods, agent-js
imports, and our actor utilities.
// hello.test.ts
;
;
;
;
Calling Hello World
Finally, we can test our canister! This method is fundamentally pretty simple, but let’s go through the exercise anyway. The test
method accepts two arguments - a test name, and a function. Inside of the test, we’ll run through some steps, and then use the expect
util to check results against expected results. See vitest docs for more info.
"should handle a basic greeting", ;
Spin up your canister, and then you can run npm test
(or bun test!) to run your new test.
You should see a success message like this in your terminal, while vitest
waits for new changes to your source code.
)
)
)
Testing CanisterStatus
For a slightly more complex test as reference, let’s add a test for canister metadata using the new CanisterStatus API.
"Should contain a candid interface", ;
Repeatable identities
It is often useful to use an identity that will remain consistent across runs of your e2e tests. Here is a script using bip39
and the industry standard “12 peacocks” test phrase.
//identity.ts
;
;
;
// Completely insecure seed phrase. Do not use for any purpose other than testing.
// Resolves to "wnkwv-wdqb5-7wlzr-azfpw-5e5n5-dyxrf-uug7x-qxb55-mkmpa-5jqik-tqe"
;
;
;
This script will reproduce an identical Principal to the same seed phrase imported through Quill into DFX.
You can verify the reproducibility by adding a test for it - identity.test.ts
// identity.test.ts
;
;
"the identity should be the same", ;
Finally, Continuous Integration
This part is boring and I don’t want to explain how to configure Github in depth. If you want to add checks to your PR’s, do the following.
- add a CI script to your
package.json
scripts.
We’ll do this with
"ci": "vitest run",
"preci": "dfx stop; dfx start --background --clean; dfx deploy; dfx generate"
This way, it automatically sets up dfx and runs the tests a single time, reporting the results.
- Add a GitHub workflow config.
Specifically, this one. Create a .github
folder and inside it, create a workflows
folder. Add e2e.yml
with these contents:
name: End to End
on:
pull_request:
types:
- opened
- reopened
- edited
- synchronize
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
ghc:
spec:
- "0.16.1"
node:
- 16
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- run: npm install
- run: echo y | sh -ci "$(curl -fsSL https://sdk.dfinity.org/install.sh)"
- run: npm run ci
env:
CI: true
REPLICA_PORT: 8000
And there you go! You should now have automated end to end tests running on your canister for every pull request.