Are there any best practices / guides available for integration testing of Rust canisters or canisters in general?
I’ve found this guide that was written a while back. Are there any other useful resources available?
If you are ok skipping the frontend I like to use the state machine for anything where unit tests are not good enough
Hello,
As each integration test has to run in a clean environment, how would you re-init each canister state between each test?
Using -Mode reinstall from dfx currently seems to be the only solution?
Do you have any other solutions/ideas?
If you use the StateMachine it’s a lot faster. But yes, reinstalling is the only way to get a clean state on a replica
I’ve never used it, do you have any information or documentation on it?
I’m waiting for one of our repos to be open-sourced so I have a nice example to show. Probably only a few days away. Setting up the binary is described here. And here’s a few snippets to get you started:
Creating a StateMachine:
fn new_state_machine() -> StateMachine {
let mut state_machine_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.to_path_buf();
state_machine_path.push("ic-test-state-machine");
if !state_machine_path.exists() {
panic!("state machine binary does not exist");
}
StateMachine::new(state_machine_path.to_str().unwrap(), false)
}
Then you can write utility functions like this:
pub fn transfer(
env: &StateMachine,
ledger_id: Principal,
from: Account,
args: TransferArgs,
) -> Result<Nat, TransferError> {
let arg = Encode!(&args).unwrap();
if let WasmResult::Reply(res) = env
.update_call(ledger_id, from.owner, "icrc1_transfer", arg)
.unwrap()
{
Decode!(&res, Result<candid::Nat, TransferError>).unwrap()
} else {
panic!("transfer rejected")
}
}
And with a bunch of those your tests can look like this:
#[test]
fn test_deposit_flow() {
let env = &new_state_machine();
let ledger_id = install_ledger(env);
let depositor_id = install_depositor(env, ledger_id);
let user = Account {
owner: Principal::from_slice(&[0]),
subaccount: None,
};
// Check that the total supply is 0
assert_eq!(total_supply(env, ledger_id), 0u128);
// Check that the user doesn't have any tokens before the first deposit.
assert_eq!(balance_of(env, ledger_id, user), 0u128);
// Make the first deposit to the user and check the result.
let deposit_res = deposit(env, depositor_id, user, 1_000_000_000);
assert_eq!(deposit_res.txid, Nat::from(0));
assert_eq!(deposit_res.balance, Nat::from(1_000_000_000));
...
You can also try to use lightic
, nodejs package. It simulates replica in nodejs context, so you can deploy and test multi canister projects (works with any canister compiled to wasm, so both rust and motko).