En esta sección puedes ver cómo escribir y ejecutar pruebas unitarias para contratos inteligentes cosmwasm.
Algunas de las mejores prácticas y consejos incluyen:
Probar funciones de consulta: Aprende a probar las funciones de consulta de forma efectiva llamándolas con varios parámetros y verificando los resultados. Esto garantiza que los usuarios puedan interactuar con el contrato inteligente según lo previsto.
Pruebe la gestión de errores y los casos extremos: Descubra cómo cubrir escenarios de manejo de errores y casos de borde en sus pruebas. Esto garantiza que el contrato inteligente se comporte correctamente en situaciones inesperadas o cuando reciba entradas inesperadas.
Pruebas con dependencias simuladas personalizadas: Comprenda cómo crear dependencias simuladas personalizadas para sus pruebas, simulando diferentes escenarios y condiciones que pueden surgir durante la ejecución del contrato inteligente. Esto ayuda a verificar el comportamiento del contrato en diferentes circunstancias.
Para aprender a realizar pruebas unitarias en contratos inteligentes cosmwasm, puedes echar un vistazo al siguiente tutorial:
En este punto, tu código debería estar compilado, aunque no hayas probado si funciona o no. Puedes desplegar el código en la blockchain cada vez que hagas un cambio, pero esto no es un uso eficiente de tu tiempo. También es importante mantener el contrato intacto y bien probado para futuras modificaciones.
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MOCK_CONTRACT_ADDR};
use cosmwasm_std::{attr, coins, CosmosMsg};
Tiene la opción de mantener sus pruebas y código en el mismo archivo o en archivos separados. Vea un ejemplo aquí.
Para cada prueba, es importante simular variables específicas, como el tiempo de bloqueo y el estado. Escribir una función para facilitar la configuración puede hacer que este proceso sea más manejable.
#[test]
fn proper_initialization() {
let mut deps = mock_dependencies(&[]);
let msg = InitMsg {
counter_offer: coins(40, "ETH"),
expires: 100_000,
};
let info = mock_info("creator", &coins(1, "BTC"));
// we can just call .unwrap() to assert this was a success
let res = init(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(0, res.messages.len());
// it worked, let's query the state
let res = query_config(deps.as_ref()).unwrap();
assert_eq!(100_000, res.expires);
assert_eq!("creator", res.owner.as_str());
assert_eq!("creator", res.creator.as_str());
assert_eq!(coins(1, "BTC"), res.collateral);
assert_eq!(coins(40, "ETH"), res.counter_offer);
}
Genial, ya tienes un inicializador de entorno de pruebas. Este es bastante básico; puedes pasar variables a la función y hacer varias modificaciones. Echa un vistazo a cosmwasm-plus para más opciones.
Información de dependencias, entorno y mensajes simulados
Hay tres herramientas de mocking que deberíamos mejorar:
/// All external requirements that can be injected for unit tests.
/// It sets the given balance for the contract itself, nothing else
pub fn mock_dependencies(
contract_balance: &[Coin],
) -> OwnedDeps<MockStorage, MockApi, MockQuerier> {
let contract_addr = HumanAddr::from(MOCK_CONTRACT_ADDR);
OwnedDeps {
storage: MockStorage::default(),
api: MockApi::default(),
querier: MockQuerier::new(&[(&contract_addr, contract_balance)]),
}
}
mock_dependencies se usa para mock storage, api, y querier.
/// Returns a default enviroment with height, time, chain_id, and contract address.
/// You can submit as is to most contracts, or modify height/time if you want to
/// test for expiration.
///
/// This is intended for use in test code only.
pub fn mock_env() -> Env {
Env {
block: BlockInfo {
height: 12_345,
time: 1_571_797_419,
time_nanos: 879305533,
chain_id: "archway-testnet-14002".to_string(),
},
contract: ContractInfo {
address: HumanAddr::from(MOCK_CONTRACT_ADDR),
},
}
}
mock_env se utiliza para bloques de imitación y entornos de contrato.
/// Just set sender and sent funds for the message.
/// This is intended for use in test code only.
pub fn mock_info<U: Into<HumanAddr>>(sender: U, sent: &[Coin]) -> MessageInfo {
MessageInfo {
sender: sender.into(),
sent_funds: sent.to_vec(),
}
}
mock_info se utiliza para simular entornos de transacción.
#[test]
fn transfer() {
let mut deps = mock_dependencies(&[]);
let msg = InitMsg {
counter_offer: coins(40, "ETH"),
expires: 100_000,
};
let info = mock_info("creator", &coins(1, "BTC"));
// we can just call .unwrap() to assert this was a success
let res = init(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(0, res.messages.len());
// random cannot transfer
let info = mock_info("anyone", &[]);
let err = handle_transfer(deps.as_mut(), mock_env(), info, HumanAddr::from("anyone"))
.unwrap_err();
match err {
ContractError::Unauthorized {} => {}
e => panic!("unexpected error: {}", e),
}
// owner can transfer
let info = mock_info("creator", &[]);
let res =
handle_transfer(deps.as_mut(), mock_env(), info, HumanAddr::from("someone")).unwrap();
assert_eq!(res.attributes.len(), 2);
assert_eq!(res.attributes[0], attr("action", "transfer"));
// check updated properly
let res = query_config(deps.as_ref()).unwrap();
assert_eq!("someone", res.owner.as_str());
assert_eq!("creator", res.creator.as_str());
}