Os testes de contrato desempenham um papel fundamental no desenvolvimento de contratos inteligentes. Eles possibilitam aos desenvolvedores verificar o comportamento do contrato em diferentes cenários.
Last updated
Was this helpful?
ink! suporta três estágios distintos de testes: testes unitários, testes de integração e testes de ponta a ponta. Nesta página, iremos explicar o propósito de cada estágio e como utilizá-los.
Geralmente, você pode visualizar esses três tipos de testes como uma pirâmide, onde o teste mais elaborado está no topo. Os testes de ponta a ponta (End-to-End - E2E) no topo testarão as camadas inferiores da pirâmide como parte de seu escopo.
Testar contratos fora da blockchain é realizado por meio do comando 'cargo test', e os usuários podem facilmente utilizar as práticas padrão do Rust para criar módulos de teste unitário dentro do projeto ink!.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn my_test() { ... }
}
Instâncias de teste de contratos podem ser criadas da seguinte maneira:
let contract = MyContract::my_constructor(a, b);
As mensagens podem ser chamadas na instância retornada da mesma forma que se MyContract::my_constructor retornasse uma instância de Self.
Para testes de integração, o teste é anotado com o atributo #[ink::test], em vez de #[test]. Essa anotação indica que o teste é executado em um ambiente simulado e emulado de blockchain. A seguir, estão disponíveis funções que permitem influenciar a configuração do ambiente de teste, como definir um saldo especificado de uma conta para simular como um contrato se comportaria ao interagir com ela.
Se você anotar um teste com o atributo #[ink::test], ele será executado em um ambiente simulado, semelhante ao que ocorreria na blockchain. Isso permite um controle detalhado sobre como um contrato é chamado, incluindo a capacidade de influenciar o avanço do bloco, o valor transferido para ele, qual conta o chama, em qual armazenamento ele é executado, entre outros aspectos.
Consulte o contrato examples/erc20 para obter uma demonstração de como utilizar essas funcionalidades, ou consulte a documentação para obter mais detalhes.
Atualmente, reconhecemos a existência de algumas limitações em nosso ambiente fora da blockchain, e estamos empenhados em aprimorá-lo para que se assemelhe o máximo possível ao ambiente real da blockchain.
NOTA: Uma limitação do framework de testes fora da cadeia é que atualmente ele suporta apenas um DefaultEnvironment.
Aqui, você encontrará uma explicação do que é um ambiente.
Como descobrir se o seu teste requer o ambiente fora da cadeia?
Normalmente, se o teste utiliza recursivamente ou invoca métodos de contratos que chamam um método definido em self.env() ou Self::env().
#[cfg(test)]
mod tests {
// Conventional unit test that works with assertions.
#[ink::test]
fn test1() {
// test code comes here as usual
}
// Conventional unit test that returns some Result.
// The test code can make use of operator-`?`.
#[ink::test]
fn test2() -> Result<(), ink::env::Error> {
// test code that returns a Rust Result type
}
}
Os testes de ponta a ponta (E2E) permitem que os desenvolvedores escrevam testes que não apenas testam o contrato de forma isolada, mas também testam o contrato em conjunto com todos os componentes envolvidos na blockchain, ou seja, do início ao fim. Essa forma de teste se assemelha de perto ao comportamento real do contrato em produção.
Como parte do teste, o contrato é compilado e implantado em um nó Lunes em execução em segundo plano. O ink! oferece funções de API que permitem que os desenvolvedores interajam com o contrato por meio de transações que eles criam e enviam para a blockchain.
Como desenvolvedor, você pode definir asserções sobre o resultado de suas transações, como verificar mutações de estado, falhas de transação ou custos de gás incorridos.
Sua configuração da blockchain será testada juntamente com o contrato inteligente. Se a sua blockchain tiver paletes que estão envolvidos na execução do contrato inteligente, eles também farão parte da execução do teste.
O ink! não impõe nenhum requisito ao nó Lunes em segundo plano - por exemplo, você pode executar um nó que contenha um snapshot de uma rede ao vivo.
O exemplo de código a seguir ilustra um teste básico de E2E para o exemplo do flipper.
#[ink_e2e::test]
async fn default_works(mut client: ink_e2e::Client<C, E>) -> E2EResult<()> {
// When the function is entered, the contract was already
// built in the background via `cargo contract build`.
// The `client` object exposes an interface to interact
// with the Substrate node.
// given
let constructor = FlipperRef::new_default();
// when
let contract_acc_id = client
.instantiate("flipper", &ink_e2e::bob(), constructor, 0, None)
.await
.expect("instantiate failed")
.account_id;
// then
let get = build_message::<FlipperRef>(contract_acc_id.clone())
.call(|flipper| flipper.get());
let get_res = client
.call(&ink_e2e::bob(), get, 0, None)
.await
.expect("get failed");
assert!(matches!(get_res.return_value(), false));
Ok(())
}
Você pode executar o teste acima indo para a pasta do flipper no diretório de exemplos do ink!.
Antes de poder executar o teste, você precisa instalar um nó Lunes com pallet-contracts. Por padrão, os testes de E2E exigem que você instale o lunes-contracts-node. Você não precisa executá-lo em segundo plano, pois o nó é iniciado independentemente para cada teste. Para instalar a versão mais recente: