Testes automatizados
Os testes automatizados utilizam ferramentas que verificam automaticamente o código de um contrato inteligente em busca de erros de execução. O benefÃcio dos testes automatizados está no uso de scripts para orientar a avaliação das funcionalidades do contrato. Esses scripts podem ser programados para serem executados repetidamente com mÃnima intervenção humana, tornando os testes automatizados mais eficientes do que os testes manuais.
Os testes automatizados são particularmente úteis quando os testes são repetitivos e demorados, difÃceis de realizar manualmente, suscetÃveis a erros humanos ou envolvem a avaliação de funções crÃticas do contrato. No entanto, as ferramentas de teste automatizado podem apresentar desvantagens: podem deixar passar certos erros e gerar muitos falsos positivos. Por isso, combinar testes automatizados com testes manuais é o ideal para contratos inteligentes.
Tipos de Testes
Testes unitários: Focam-se principalmente em funções e componentes individuais. Cada função opera com base em determinadas suposições sobre os parâmetros que recebe e o estado do sistema com o qual interage. Por isso, os testes unitários devem abranger todos os cenários possÃveis — tanto válidos (o que o contrato deve fazer) quanto inválidos (o que o contrato não deve fazer) — nos quais uma função pode ser chamada, a fim de identificar erros de lógica.
Tenha clareza sobre quais são as funções do contrato e como os usuários irão acessá-las e utilizá-las.
Avalie todas as suposições relacionadas à execução do contrato. O que o contrato pode e o que não pode fazer?
Meça a cobertura do código. Quanto maior, melhor.
Utilize ferramentas de teste robustas e bem consolidadas no mercado.
Testes de integração: Constituem a camada seguinte de testes, sendo construÃdos sobre os testes unitários. Após confirmar a funcionalidade dos componentes individuais, verifica-se sua integração. Este nÃvel foca nas interações entre os componentes, sejam eles internos ou externos. Por exemplo, avalia se um sistema se comporta corretamente ao chamar um serviço de terceiros. Os testes de integração são essenciais, pois essas relações não são verificadas nos testes unitários.
Os testes de integração são especialmente úteis quando o contrato adota uma arquitetura modular ou interage com outros contratos on-chain durante a execução. Uma forma comum de realizar testes de integração é fazer um fork da mainnet em uma altura especÃfica utilizando ferramentas como Hardhat e Foundry, e simular interações entre os nossos contratos e os contratos já implantados.
O fork se comporta de forma semelhante à mainnet e possui contas com estados e saldos associados. No entanto, atua apenas como um ambiente de desenvolvimento local isolado, o que significa que não será necessário utilizar ETH real para as transações e que quaisquer alterações realizadas não afetarão o ambiente real da rede Ethereum.
Testes baseados em propriedades: Os testes baseados em propriedades são o processo de verificar se um contrato inteligente cumpre com alguma propriedade definida. As propriedades afirmam fatos sobre o comportamento de um contrato que se espera que se mantenham verdadeiros em diferentes cenários. Um exemplo de uma propriedade de um contrato inteligente poderia ser: "As operações aritméticas no contrato nunca causarão overflow ou underflow."
A análise estática e a análise dinâmica são duas técnicas comuns para executar testes baseados em propriedades, e ambas podem verificar se o código de um contrato cumpre com alguma propriedade predefinida. Algumas ferramentas de testes baseados em propriedades vêm com regras predefinidas sobre as propriedades esperadas do contrato e verificam o código em relação a essas regras, enquanto outras permitem criar propriedades personalizadas para um contrato inteligente.
Análise estática
Um analisador estático recebe como entrada o código-fonte de um contrato inteligente e gera resultados declarando se o contrato satisfaz ou não uma propriedade. Ao contrário da análise dinâmica, a análise estática não envolve a execução de um contrato para analisá-lo quanto à sua correção. Em vez disso, a análise estática razoa sobre todos os possÃveis caminhos que um contrato inteligente pode seguir durante a execução (ou seja, examina a estrutura do código-fonte para determinar o que isso significaria para a operação do contrato em tempo de execução). O linting (verificação automatizada do código em busca de erros programáticos e estilÃsticos) e os testes estáticos são métodos comuns para realizar análise estática em contratos. Ambos exigem analisar representações de baixo nÃvel da execução de um contrato, como árvores de sintaxe abstrata e gráficos de fluxos de controle produzidos pelo compilador.
Na maioria dos casos, a análise estática é útil para detectar problemas de segurança, como o uso de construções inseguras, erros de sintaxe ou violações de padrões de codificação no código de um contrato. No entanto, é sabido que os analisadores estáticos geralmente são pouco confiáveis para detectar vulnerabilidades mais profundas e podem gerar muitos falsos positivos.
Análise dinâmica
A análise dinâmica gera entradas simbólicas ou entradas concretas para as funções de um contrato inteligente para verificar se algum caminho de execução viola propriedades especÃficas. Essa forma de testes baseados em propriedades difere dos testes unitários, pois os casos de teste cobrem múltiplos cenários, e um programa gerencia a criação dos casos de teste.
O fuzzing é um método automatizado que injeta entradas inválidas ou inesperadas em um sistema, revelando vulnerabilidades de segurança. Utiliza ferramentas especializadas que variam conforme a tecnologia. Um fuzzer invoca funções em um contrato alvo com variações aleatórias ou incorretas de um valor de entrada definido. Se o contrato inteligente entra em um estado de erro (por exemplo, onde uma asserção falha), o problema é sinalizado, e as entradas que levam ao erro são registradas em um relatório.
Essa forma de testes baseados em propriedades pode ser ideal por várias razões:
É difÃcil escrever casos de teste para cobrir muitos cenários. Um teste de propriedade requer apenas que você defina um comportamento e um intervalo de dados para testar o comportamento; o programa gera automaticamente casos de teste com base na propriedade definida.
Seu conjunto de testes pode não cobrir suficientemente todos os possÃveis caminhos dentro do programa. Mesmo com uma cobertura de 100%, é possÃvel deixar de cobrir casos extremos.
Os testes unitários demonstram que um contrato executa corretamente para dados de uma amostra, mas se o contrato executará corretamente para entradas fora dessa amostra ainda é desconhecido. Os testes de propriedades executam um contrato alvo com múltiplas variações de um valor de entrada dado para encontrar caminhos de execução que causam falhas nas asserções. Portanto, um teste de propriedade fornece mais garantias de que um contrato será executado corretamente para uma ampla classe de dados de entrada.
Last updated