O que são testes de mutantes?

Como garantir que nossa bateria de testes está boa? Essa é uma pergunta importante, afinal de nada adianta termos testes automatizados se eles não forem bons o suficiente. Uma maneira bem conhecida é calcular a cobertura do código. Ferramentas como o Cobertura dizem pra você quais linhas do seu código de produção não são executadas pela sua bateria de teste. Assim, você pode facilmente ver qual if você esqueceu de testar, e escrever um teste pra ele.

Mas essa não é a única maneira. Uma delas, bastante interessante e muito popular na academia (mas não tão popular na indústria) é o teste de mutante. A ideia é genial. Imagine que tenhamos testes verdes para o seguinte trecho de código:

Veja que o if verifica se a é MAIOR que 10. O que deveria acontecer com os testes se mudássemos o sinal de MAIOR para MENOR? Nossa bateria de testes deveria quebrar, certo? Pois é exatamente isso que testes de mutantes fazem. Um mutante é simplesmente uma versão alterada do seu código de produção. Alterar o sinal de maior para menor é uma maneira de gerar um mutante. Mudar o sinal de + pra -, * por /, e assim por diante. E a ideia é que, se rodarmos os testes trocando a classe por um mutante, algum teste deve falhar. Afinal, o mutante faz coisa errada!

Ferramentas como o Pitest pegam seu código de produção, geram diferentes mutantes, e executam a bateria de testes. Se algum teste falha, dizemos então que o mutante foi morto. Agora, se algum mutante sobrevive, então você precisa melhorar seus testes.

Para exemplificar, veja meu repositório pitest-fizzbuzz. Ele tem uma simples implementação do jogo Fizz Buzz. É o mesmo exemplo que usei na pequena série de começando com testes, aqui no meu blog. Você entenderá o jogo pelo código:

Veja os testes dele (que fizemos juntos no blog):

A bateria de testes parece bem completa. Veja o resultado do relatório emitido pelo Pitest. Tudo verde, ou seja todos os mutantes foram mortos (ahá, eu mandei bem quando escrevi os testes! 🙂

pitest-sucesso

Agora, vamos supor que tivéssemos esquecido o teste deveRetornarFizzBuzzQuandoMultiploDe5e7(). Olha só o relatório. Ele nos mostra bem a linha do código de produção onde ele fez a mutação (nesse caso, as mutações), a descrição, e mostra que o mutante sobreviveu. Nesse caso, ele mudou o % por *, e nenhum teste pegou.

pitest-erro

Até aí, parece que o cobertura também pegaria isso. Afinal, essa linha não foi coberta. No entanto, testes de mutantes vão além. Mas imagine uma nova regra no jogo: Números maiores ou iguais a 500 imprimem “big”. Implementamos:

E adicionamos o teste:

Agora, a cobertura desse código é 100%.

pitest-cobertura

No entanto, veja o teste de mutante. Ele pegou! Afinal, não testamos o caso do igual no maior-ou-igual. Uma mutação ali (trocar por exemplo para só maior), e o mutante sobrevive!

pitest-erro3

Testes de mutantes são bastante interessantes, e ainda muito desafiadores para a academia. Afinal, em um sistema grande, o número de mutantes pode ser muito grande. Como encontrar os melhores mutantes? Como remover mutantes redundantes? No site do pitest, você pode ler um pouco mais sobre mutantes e ver as operações de mutação que ele implementa.

A ferramenta tem plugin para o Maven, então é fácil de executar. No repositório de exemplo, o pom.xml está configurado.

2 thoughts on “O que são testes de mutantes?

  1. Henrique Luz

    Oi Aniche, ferramenta bem bacana, não conhecia.
    Como você bem disse, também me pergunto se o overhead não seria muito grande dado uma aplicação real com uma base de código bem extensa.

    Acredito que você já tenha lido algo sobre branch coverage ou complexity coverage usados pelo JaCoCo. Tenho utilizando essas métricas e têm me ajudado bastante no dia-a-dia.
    Qual sua opinião a respeito?

    Valeu!

    Reply
    1. mauricioaniche Post author

      Oi Henrique,

      Elas ajudam, sim! Mas mutantes conseguem ir além do que conseguir ver complexity coverage (que foi meu exemplo aqui)! Veja os operadores de mutação do site do pitest!

      A ferramenta de code coverage, com certeza, executará muito mais rápido. Essa é uma vantagem forte, IMHO! Acho que as duas técnicas se complementam!

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *