Teste Mutante — seus testes são efetivos? - Blog 4ALL Tests
Carregando:

Teste Mutante — seus testes são efetivos?

Teste Mutante — seus testes são efetivos?

Teste Mutante — seus testes são efetivos?

Quando você vê esse nome no que você pensa? Sim eu também. Mas aqui, diferente dos X-Men, os mutantes são ruins, e o nosso objetivo é matá-los. Ainda estamos falando de testes? Por incrível que pareça, sim, e eu vou explicar.

Quem nunca pensou: “Esse teste podia ficar melhor, mas agora não tenho tempo para melhorar”? Ou abriu um teste de um colega e viu que ele não testava nada? O que o teste mutante faz é o “teste do teste”, ou seja, a efetividade do teste.

Pensa o seguinte: um programa que gera um ser humano comum. Temos quatro testes pra eles: se tem uma cabeça, se tem dois braços, se tem duas pernas, se tem um tronco. Se pensarmos em cobertura de teste, todo o corpo foi coberto. Mas será que os testes são efetivos? É isso que o teste mutante verifica. Ele altera o programa criando “monstrinhos” e os testes são os responsáveis por “matá-los”. Exemplo:


Primeiro mutante

O teste da cabeça passa, mas os outros testes matam o mutante.


Segundo mutante

O teste da cabeça passa, o do tronco também, mas os outros dois testes matam o mutante.


Terceiro mutante

Todos os testes passam, mas não é um humano. Temos um “mutante” vivo.

Como funciona na prática?

Vamos pegar um projeto simples, com agência e cliente. O projeto possui o cliente com nome e número da conta, e agência, que agrupa esses clientes.


Classe cliente

Classe agência

Você precisa subir a classe cliente, mas se esqueceu de fazer o teste unitário. Como o prazo está apertado, você faz uma chamada de construção e sobe o projeto. 39% de cobertura.


Baixa cobertura

As diretrizes da empresa dizem que para subir é necessário no mínimo 70% de cobertura então, após verificar o código do teste, você usa todos os gets e sets, assim consegue 100% de cobertura e seu programa sobe lisinho para produção.


Cobertura 100%! Pronto para a produção

Mas obviamente esse teste é muito pobre, mesmo sendo uma classe simples. Agora digamos que você tem milhares de módulos, como saber a efetividade desses testes, além da cobertura?

É aí que entra o teste mutante. O que o teste mutante faz é muito simples. Ele cria mutações do seu código (ex. se o if é a > b, ele muda pra a < b), e roda o teste novamente. Se o teste quebrou, o mutante morreu, mas se o teste não quebrar, temos um mutante vivo, ou seja, um programa que potencialmente poderia ser testado de forma mais efetiva.

Vamos à prática. No nosso exemplo, como poderíamos testar algumas mutações? Você não precisa se preocupar com isso, pois o próprio plugin já possui mutações pré-definidas. Então vamos instalar o teste mutante no nosso eclipse. Entre no menu Help > Eclipse Marketplace. No Marketplace você vai procurar por Pitclipse:


PITest

Instale e reinicie o eclipse. Agora quando vc clicar com o botão direito no teste, e for no menu “Run As” vai aparecer a opção PIT Mutation Test.


Novo menu no "Run As"

Agora vamos executá-lo, e ver o que ele fala do nosso teste.


A carinha do PITest

Como era de se esperar (pela pobreza do teste) ele diz que todos os mutantes sobreviveram. Vamos analisar o motivo:


2 mutantes vivos!

O que o plug-in fez? Substituiu o “return conta” por “return 0”, e o teste continuou funcionando (óbvio, pois o teste nem assert tem).

O plug-in sugere onde testar, trazendo uma luz sobre a qualidade do seu software.

Vamos melhorar o teste agora:


Morreu um mutante, só falta um

Legal, já matamos um mutante (nada pessoal, Xavier). Agora vamos melhorar para cobrir o outro mutante:


Pronto. Você pode ver que, como eu apaguei as linhas do “set”, a cobertura não foi 100%, mas aí você sabe que já testou a área do teste de maior risco. Se for do seu interesse, pode incluir os sets, e assim ter cobertura total e mutantes mortos.

Tá, Alex, mas isso é muito simples. Ok, esse exemplo é bem didático. Vou trazer outro um pouco mais complexo, que vai mostrar como a cobertura pode esconder situações não testadas. No exemplo abaixo temos a classe “Agencia.java”. Ela é um agrupador de clientes, com função de adicionar e remover. Vamos pensar na mesma situação: entrega, correria, falta de testes. Como você tá mais esperto agora, monta um cenário, criando os clientes, incluindo na agência, removendo um e consultando o outro. Faz um teste também para o caso de agência nula:


Cobertura boa e testes bons. Será?

Você já tem o % necessário, e o programa vai pra rua. Sobrou um tempinho e vc roda o pitest e nota que, apesar da cobertura estar acima de 80%, metade dos mutantes sobreviveram! Abrindo a análise o primeiro que te chama é o da linha 42:


Não há nenhuma verificação se o cliente realmente foi removido. Com uma linha de código matamos dois mutantes! Mas como não é ideal que um teste só faça várias verificações, vamos construir outro teste para verificar exclusão de clientes.


De pouquinho em pouquinho a gente mata todos!

E quanto àqueles dois mutantes ainda vivos? Analisando a mensagem, vemos que colocando fixo “true” na saída do removeCliente, o mutante fica vivo. Claro, porque não testamos o caso de uma exclusão sem sucesso. Vamos criar esse teste:


Sem mutantes no meu turno! (Já me sinto um sentinela)

Beleza! Todos os mutantes mortos! O teste mutante te ajudou a focar onde o teste pode ser mais necessário.

Concluindo: quando pensamos em testes precisamos focar em risco e probabilidade. Cada vez que fechamos um pacote temos os recursos que nos dão informação sobre a saúde do software.

O teste mutante se apresenta como mais um recurso para a tomada de decisão.

E serve também para verificar o que faltou de testes em termos negociais, depois que vc já fez o TDD e construiu seu código. São excelentes para verificar as regras de negócio, um teste de mesa (chamado aqui em Brasília de "teste chinezinho") automatizado pra não deixar passar nada.

Espero que vocês tenham gostado. Pretendo ainda escrever sobre como colocar o teste mutante em uma esteira CI, até lá!


Compartilhe :
   
Tags :
Testes   QA   Dicas  

Alex Steeve