Sumário
- Por que teste de mutação?
- Teste de mutação para o resgate
- Operadores de mutação
- Avaliando operadores de mutação
- Mutações equivalentes
- Para concluir
9 de outubro de 2019
Recentemente, eu lancei uma ferramenta de teste de mutação para contratos inteligentes no GitHub, chamada Vertigo.
Nesta série de posts do blog, espero fazer as seguintes coisas:
- Mostrar o quão poderoso é o teste de mutação;
- Explicar como usar testes de mutação no seu SDLC (Software Development Life Cycle ou Ciclo de vida do desenvolvimento do software);
- Demonstrar como você pode usar a Vertigo em alguns exemplos de projeto.
A Vertigo é acompanhada por um trabalho de pesquisa que foi apresentado na CBT’19.
# Por Yves Alarie
Por que teste de mutação?
Nesse primeiro post, revisaremos os conceitos e, mais importante, a motivação por trás do teste de mutação.
Não faltam histórias sobre os incidentes de segurança que ocorreram com sistemas de contratos inteligentes (o hack da DAO, hack da carteira Patiry, batchOverflow,...). O ambiente de altas apostas e alto risco estimulou o desenvolvimento de muitas ferramentas e técnicas que ajudam a aumentar a segurança de projetos de contrato inteligente (por exemplo, Mythril, a plataforma MythX, a estrutura K, Verisol, etc.).
Muitas dessas ferramentas merecem seus lugares no seu ciclo de vida de desenvolvimento, melhorando a segurança de contratos inteligentes em todo o espectro.
Outro método que já está sendo ativamente aplicado em desenvolvimento de contrato inteligente é o teste de unidade.
Testes de unidade podem ser usados para garantir que um programa ou contrato inteligente funcione conforme o esperado em um conjunto de entradas concretas. Embora isso não exclua a presença de bugs, um conjunto de testes aprovado dá uma sensação de confiança na correção e segurança de um contrato inteligente.
Isso levanta a questão: o quão confiante passar em um conjunto de testes deveria fazer você se sentir?
A maior parte das equipes de desenvolvimento usa a cobertura de código como uma métrica para responder a essa pergunta.
Como sugere o nome, a cobertura de código conta a porcentagem de linhas, instruções, ramificações, etc. cobertas pelos testes em um conjunto de testes.
Infelizmente, existem alguns problemas com esta métrica:
- Em primeiro lugar, pode-se escrever testes que cubram muito código de uma só vez; eles podem melhorar a cobertura de código medida sem realmente adicionar muitas garantias. A qualidade do teste parecerá aumentar, mas, na realidade, permanece a mesma.
- Em segundo lugar, os testes de unidade podem precisar de asserções adequadas. Como resultado, partes do código podem parecer bem cobertas, enquanto a própria lógica de negócios é insuficientemente testada.
Resumindo, você não deve usar a cobertura de código como uma métrica para a segurança ou correção de seus contratos inteligentes.
# Por Yogi Purnama
Teste de mutação para o resgate
Teste de mutação é uma abordagem que pode auxiliar com a avaliação da qualidade de um conjunto de testes.
Ele especificamente tenta responder à seguinte pergunta:
”Quão bom é este conjunto de testes em encontrar bugs nos contratos inteligentes?”
# Por Amelia Barklid
Ele o faz gerando versões ligeiramente alteradas do contrato inteligente, chamadas mutantes.
Cada um desses mutantes representa um bug em potencial nos contratos inteligentes.
Para cada mutante, podemos verificar se pelo menos um dos testes falha (isso é chamado de “matar um mutante”).
Operadores de mutação
Os mutantes são gerados baseados em estratégias de mutação, chamadas de “operadores de mutação”.
Um operador de mutação implementa regras de tradução específicas que tentam introduzir um comportamento defeituoso.
A figura a seguir mostra algumas regras de mutação que transformam operadores de comparação em seu exato oposto.
Por exemplo, um operador de iguais se torna um operador não-iguais.
< => >=
> => <=
<= => >
>= => <
!= => ==
== => !=
Exemplo de regras de mutação
A Vertigo implementa uma série desses operadores de mutação visando diferentes partes do contrato, como operações aritméticas, modificadores e comparações.
Avaliando operadores de mutação
Determinar como uma mutação é morta (uma mutação é morta quando um dos testes falha para a mutação) ou não é bem direto. Você executa o conjunto de testes em um programa com mutação e descobre se um dos testes falha.
Contudo, apesar de um teste sobreviver ou ser morto, podemos acabar em duas situações:
- A primeira, a classe adicionada está esgotada; encontramos esse caso de mutações em situações em que um mutante cria um loop infinito ou, caso contrário, desacelera muito a execução de um conjunto de testes.
- A segunda, a classe está com erro, o que pode ocorrer sempre que a compilação do mutante não foi bem-sucedida ou a estrutura de teste encontrou algum erro inesperado (além de um teste com falha, claro).
Mutações equivalentes
Algumas vezes, uma versão com mutação de um programa é equivalente ao programa original.
Tome como exemplo a função max do contracts/math/math.sol do openzeppelin e sua função com mutação mutated_max().
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
function mutated_max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
Mesmo que haja uma mudança sintática no programa (“>=” é alterado para “>”), ele ainda dará os mesmos resultados corretos para todas as entradas possíveis.
Como resultado, este mutante “sobreviverá” a uma execução do conjunto de testes. Na avaliação do desempenho de um conjunto de testes, preferimos ignorar esses casos, pois não há nenhuma falha real introduzida no contrato inteligente testado.
Esses mutantes são chamados “mutações equivalentes” e eles formam uma das principais fraquezas dos testes de mutação. As abordagens existentes para a detecção automática dessas mutações equivalentes não são perfeitas e, muitas vezes, um desenvolvedor terá que revisar os resultados do teste de mutação para filtrar as mutações equivalentes.
Para concluir
Depois de gerar, testar e filtrar todas as mutações equivalentes, acabamos com um número de mutantes sobreviventes. Usando esse número, podemos calcular a pontuação de mutações:
Podemos usar essa pontuação de mutação para responder a pergunta do início desse post.
”Quão bom é este conjunto de testes para encontrar bugs nos contratos inteligentes.”
Além disso, os mutantes sobreviventes fornecem informações valiosas sobre quais partes do código foram insuficientemente testadas.
Obrigado por ler isso até o fim!
Neste post, vimos os principais conceitos usados no teste de mutação: operadores de mutação, mutações mortas ou sobreviventes, mutações equivalentes e pontuação de mutação.
No post a seguir, mostrarei como aplicar esses conceitos usando a Vertigo.
Grato a tintin e ao Bernhard Mueller por fornecer feedback sobre os rascunhos deste artigo.
Esse artigo foi escrito por Joran Honig e traduzido por Isabela Curado Nehme. Seu original pode ser lido aqui.
Abrace a oportunidade de elevar sua jornada de desenvolvimento para um nível superior. Testes de mutação é apenas o começo; os builds incríveis da WEB3DEV representam a chave de entrada para o emocionante cenário web3. 🚀🧑💻
Não perca tempo, 👉inscreva-se👈 agora mesmo e comece a desbravar o universo Blockchain!
Seja também WEB3DEV!
Oldest comments (0)