Testing a query
Last time we created a new query. Now it is time to test it out. We will start with the basics -
the unit test. This approach is simple and doesn't require much knowledge besides Rust. Go to the
src/contract.rs
and add a test at the bottom of the file:
use crate::responses::AdminListResp;
use cosmwasm_std::{Addr, Deps, DepsMut, Empty, Env, MessageInfo, Order, Response, StdResult};
use cw_storage_plus::Map;
use schemars;
use sylvia::contract;
pub struct AdminContract<'a> {
pub(crate) admins: Map<'a, &'a Addr, Empty>,
}
#[contract]
impl AdminContract<'_> {
pub const fn new() -> Self {
Self {
admins: Map::new("admins"),
}
}
#[msg(instantiate)]
pub fn instantiate(
&self,
ctx: (DepsMut, Env, MessageInfo),
admins: Vec<String>,
) -> StdResult<Response> {
let (deps, _, _) = ctx;
for admin in admins {
let admin = deps.api.addr_validate(&admin)?;
self.admins.save(deps.storage, &admin, &Empty {})?;
}
Ok(Response::new())
}
#[msg(query)]
pub fn admin_list(&self, ctx: (Deps, Env)) -> StdResult<AdminListResp> {
let (deps, _) = ctx;
let admins: Result<_, _> = self
.admins
.keys(deps.storage, None, None, Order::Ascending)
.map(|addr| addr.map(String::from))
.collect();
Ok(AdminListResp { admins: admins? })
}
}
#[cfg(test)]
mod tests {
use crate::entry_points::{instantiate, query};
use cosmwasm_std::from_binary;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use super::*;
#[test]
fn admin_list_query() {
let mut deps = mock_dependencies();
let env = mock_env();
instantiate(
deps.as_mut(),
env.clone(),
mock_info("sender", &[]),
InstantiateMsg {
admins: vec!["admin1".to_owned(), "admin2".to_owned()],
},
)
.unwrap();
let msg = QueryMsg::AdminList {};
let resp = query(deps.as_ref(), env, ContractQueryMsg::AdminContract(msg)).unwrap();
let resp: AdminListResp = from_binary(&resp).unwrap();
assert_eq!(
resp,
AdminListResp {
admins: vec!["admin1".to_owned(), "admin2".to_owned()],
}
);
}
}
We have a simple flow here:
- we instantiate contract with admins ["admin1", "admin2"],
- we query the contract to see if both of them will be returned
Our instantiate
, and query
require deps and env as parameters. We will mock them with
mock_dependencies
and mock_env
from
comswasm-std
.
You may notice the dependencies mock if of a type
OwnedDeps
instead
of Deps
, which we need here - this is why the
as_ref
function is called on it. If we needed a DepsMut
object, we would use
as_mut
instead.
I extracted the deps
and env
to variables
and passed them to calls. The idea is that those represent some blockchain persistent state,
and we don't want to create them for every call. We want any changes to the contract state occurring
in instantiate
to be visible in the query
. Also, we want to control how the environment differs
in the query and instantiation.
The info
argument is another story. The message info is unique for each message sent. To create
the info
mock, we must pass two arguments to the
mock_info
function.
First is the address performing a call. It may look strange to pass sender
as an address instead
of some mysterious wasm
followed by hash, but it is a valid address. For testing purposes, such
addresses are typically better, as they are way more verbose in case of failing tests.
The second argument is the funds
that are sent with the message. For now, we leave it as an empty
slice, as I don't want to talk about token transfers yet - we will cover it later.
So now it is more like a real-case scenario. I see just one problem. Nothing connects the instantiate
call to the corresponding query
. It seems that we assume there is some global contract. But if we
would like to have two contracts instantiated differently in a single test case, it
would become a mess. If only some tool could abstract this for us, wouldn't it be nice?