Introducing multitest
Let me introduce the multitest
-
library for creating tests for smart contracts in Rust.
The core idea of multitest
is abstracting an entity of contract and
simulating the blockchain environment for testing purposes. The purpose of this
is to be able to test communication between smart contracts. It does its job
well, but it is also an excellent tool for testing single-contract scenarios.
Update dependencies
First, we need to add sylvia with mt
feature enabled to our
dev-dependencies
.
[package]
name = "contract"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
cosmwasm-std = { version = "2.0.4", features = ["staking"] }
sylvia = "1.1.0"
schemars = "0.8.16"
cosmwasm-schema = "2.0.4"
serde = "1.0.180"
cw-storage-plus = "2.0.0"
[dev-dependencies]
sylvia = { version = "1.1.0", features = ["mt"] }
cw-multi-test = { version = "2.1.0", features = ["staking"] }
Creating a module for tests
Now we will create a new module, multitest
. Let's first add it to the src/lib.rs
pub mod contract;
#[cfg(test)]
pub mod multitest;
pub mod responses;
As this module is purely for testing purposes, we prefix it with
#[cfg(test)]
.
Now create src/multitest.rs
.
use sylvia::cw_multi_test::IntoAddr;
use sylvia::multitest::App;
use crate::contract::sv::mt::{CodeId, CounterContractProxy};
#[test]
fn instantiate() {
let app = App::default();
let code_id = CodeId::store_code(&app);
let owner = "owner".into_addr();
let contract = code_id.instantiate(42).call(&owner).unwrap();
let count = contract.count().unwrap().count;
assert_eq!(count, 42);
}
Sylvia generates a lot of helpers for us to make testing as easy as possible.
To simulate blockchain, we create sylvia::multitest::App
. Then we will use it to store the code id
of our contract on the blockchain using sylvia generated CodeId
.
Code id identifies our contract on the blockchain and allows us to instantiate the contract on it.
We do that using CodeId::instantiate
method. It returns the InstantiateProxy
type, allowing us
to set some contract parameters on a blockchain. You can inspect methods like with_label(..)
,
with_funds(..)
or with_admins(..)
. Once all parameters are set you use call
passing caller to
it as an only argument. This will return Result<ContractProxy, ..>
. Let's unwrap
it as it is a
testing environment and we expect it to work correctly.
Now that we have the proxy type we have to import CounterContractProxy
to the scope.
It's a trait defining methods for our contract.
With that setup we can call our count
method on it. It will generate appropriate
QueryMsg
variant underneath and send to blockchain so that we don't have to do it ourselves and
have business logic transparently shown in the test. We unwrap
and extract the only field out of
it and that's all.
Next step
We tested our contract on a simulated environment. Now let's add some fluidity to it and introduce execute messages which will alter the state of our contract.