PocketIC: A Canister Testing Platform
PocketIC is the local canister smart contract testing platform for the Internet Computer.
It consists of the PocketIC server, which can run many independent IC instances, and a client library (this crate), which provides an interface to your IC instances.
With PocketIC, testing canisters is as simple as calling rust functions. Here is a minimal example:
use candid;
use pocket_ic;
Examples
For a simple but complete example with the counter canister, see here.
For larger test suites with more complex test setups, consider the Internet Identity and the OpenChat integration test suites.
Getting Started
- Download the PocketIC binary for Linux or MacOS and make it executable.
- Write the binary path into the environment variable
POCKET_IC_BIN. You can also prependPOCKET_IC_BIN=...to yourcargo testinvocation. - Add the pocket-ic crate to your Cargo.toml, or install it with
cargo install pocket-ic. - Depending on your use case, you may also need the candid crate.
Why PocketIC?
Canister developers have several options to test their software, but there are tradeoffs:
- Install and test on the mainnet: The 'real' experience, but you pay with real cycles.
- The replica provided by DFX: You get the complete stack of a single IC node. But therefore, you get no cross- or multisubnet functionality, and likely never will. Replica is quite heavyweight too, because the nonessential components are not abstracted away. Furthermore, testing with replica is not deterministic.
Enter PocketIC:
- Deterministic: Synchronous control over the IC's execution environment
- Lightweight: Mocks the consensus and networking layers
- Versatile: Runs as a service on your test system, and accepts HTTP/JSON. This enables:
- Concurrent and independent IC instances by default - sharing is possible
- Multi-language support: Anyone can write an integration library against the PocketIC REST-API in any language
- [Will support saving and loading checkpoints]
- [Will support multi-subnet IC instances]
How to use this library
You create an empty IC instance by instantiating PocketIc:
let pic = new;
This constructor will discover an already running instance of the PocketIC Server or launch a fresh one. It then requests a fresh instance and serves as a unique reference to that instance. When the value is dropped, the instance on the PocketIC Server will be deleted.
This design promotes test isolation, and we recommend to use one PocketIc instance per test. However, it is still possible to share a PocketIc instance between tests, but you do so at your own risk concerning 1) determinism and 2) performance (concurrent tests may block each other).
Using a value of the PocketIc struct, you interact with the IC itself, e.g. via:
// IC interface excerpt
...
and you interact with the canisters you have created:
// Canister interface excerpt
..>
...
You can also use your canister's candid interface like this:
let MyResult = call_candid;
Note that you have to provide your method arguments (arg1, arg2) as a tuple, because it will be encoded to candid automatically. Similarly for the return value, call_candid tries to decode the candid-encoded reply from the canister to your rust struct. For general info on candid, see here and for candid in rust here.
See the examples for more.
Contributing
Would you like to write canister tests in a different language? Using PocketIC, you can! The PocketIC binary has a JSON/REST interface, against which you may implement any user-facing library in any language. See for example the Python library.
If you decide to contribute, we encourage you to announce it on the Internet Computer Developer Forum.