Teste de mutação com Pitest
Como o próprio Pitest se auto intitula, é uma ferramenta que provê o estado da arte para testes de mutação. Foi escrito para rodar em aplicações Java ou em aplicações que rodam em cima de uma JVM. Como funciona? O Pitest funciona gerando códigos mutantes que farão alterações nos bytecodes no código fonte, alterando lógicas, removendo e até alterando valores de retorno de métodos. Dessa forma é possível avaliar o código de uma forma mais completa procurando falhas e fazendo com que o código seja confiável. Exemplo básico de mutação Veja o código abaixo: if (nome == "" ){
return "Nome vazio" ;
} else {
return nome;
} Ao rodar o Pitest no código acima, ele é capaz de modificar os bytecodes alterado o código acima para: if (nome != "" ){
return nome;
} else {
return "Nome vazio" ;;
} Desta forma ele consegue identificar possíveis pontos de falhas e te orientar a criar testes com base nestes pontos. Vamos a prática Maven < dependencies >
< dependency >
< groupId >junit</ groupId >
< artifactId >junit</ artifactId >
< version >4.12</ version >
< scope >test</ scope >
</ dependency >
< dependency >
< groupId >org.pitest</ groupId >
< artifactId >pitest</ artifactId >
< version >1.6.2</ version >
</ dependency >
</ dependencies > Vamos criar uma classe simples representando um conta de banco chamada BankAccount e para ser mais simples vamos implementar a lógica dentro da própria classe de entidade. Nela temos um método chamada userValid que verifica se o usuário é válido. package com.bank.entity;
public class BankAccount {
public String bankName ;
public String user ;
public Double balance ;
public Boolean userValid(BankAccount bankAccount){
if (bankAccount. user != "" ){
return true ;
} else {
return false ;
}
}
} Sem criar nenhuma classe de teste, execute este comando via Maven: mvn org.pitest:pitest-maven:mutationCoverage Veja que no console foi gerado um relatório com as mutações criados com base nas condições que devem ser testadas. Para cada linha acima, foi gerado mutantes e nenhum foi "morto" ainda, ou seja, precisamos criar testes que cubram estes pontos de falhas até que todos os mutantes criados sejam eliminados. Assim o nosso código terá uma cobertura melhor e sem chances de falhas. Por fim a estatística final, no qual foi criado 3 mutações e nenhuma foi eliminado. Outra forma bem interessante de verificar as coberturas e entender melhor estas estatísticas são os relatórios gerados em HTML pelo Pitest. Na pasta target do projeto, acesse a pasta pit-reports . Lá nesta pasta vai ter todos os relatórios que são criados a cada execução dos teste de cobertura do Pitest. No arquivo index.html mostra um overview do que foi coberto ou não pelos testes, no nossa caso não existe cobertura. E no arquivo BankAccount.java.html mostra detalhes do código não coberto Perceba que na imagem acima existe uma lista de Active mutators que são as mutações com base no código que criamos. Deixando bem transparente o que o Pitest criou para testar o código. Eliminando as mutações Com base nos relatórios, vamos criar alguns testes para que a cobertura chegue a 100%. Vamos criar a classe de teste chamada BankAccountTest.java e para isso existe um ponto de atenção. Crie no diretório de src/test/java , o mesmo nome do pacote criado no diretório main ( com.bank.entity ). package com.bank.entity;
import org.junit.Assert;
import org.junit. Test ;
public class BankAccountTest {
@Test
public void userValid_NegateConditionalsMutator_Test(){
BankAccount bankAccount = new BankAccount(
"Chase","Jonas" , 2000.00);
Boolean actual = bankAccount.userValid(bankAccount);
Boolean expected = true ;
Assert. assertEquals (expected, actual);
}
@Test
public void userValid_BooleanFalseReturnValsMutator_Test(){
BankAccount bankAccount = new BankAccount(
"Chase","" , 2000.00);
Boolean actual = bankAccount.userValid(bankAccount);
Boolean expected = false ;
Assert. assertEquals (expected, actual);
}
} Nos testes acima, criamos apenas 2 testes para a cobertura: 1.userValid_NegateConditionalsMutator_Test() : Faz a cobertura para nome de usuários preenchidos, ou seja, no construtor foi passado o argumento "Chase" representando o nome do usuário para que a condição do método seja validada. E por fim valida a condição do método ser retornado como TRUE. Neste caso, já eliminamos 2 mutações criadas de acordo com o relatório de estatística. BooleanTrueReturnValsMutator NegateConditionalsMutator 2. userValid_BooleanFalseReturnValsMutator_Test() : Valida a possibilidade do método retornar o valor Booleano FALSE. Execute novamente o comando Maven abaixo: mvn org.pitest:pitest-maven:mutationCoverage Console da aplicação Perceba que para cada mutação gerada, 1 foi eliminada. Cobrindo 100% da cobertura nos testes. Referências: https://pitest.org Material de estudo Se quer aprender mais sobre o assunto e alcançar um alto nível de conhecimento, recomendo fortemente a leitura do(s) seguinte(s) livro(s): Use a cabeça: Java é um clássico onde qualquer iniciante no assunto deve ler e estudar. Neste livro você aprenderá sobre os principais pilares de Java como, orientação a objetos, criação de classes, interfaces e seu funcionamento, controlar exceções, objetos genéricos e muito mais. Se quer entrar de cabeça neste mundo, é uma excelente opção! Padrões de Projeto é um livro no qual recomendamos após alcançar uma certa maturidade em programação em geral. Escrito por grandes nomes do assunto e traduzido para o português, Padrões de Projeto ou Design patterns como também são conhecidos, são padrões e estratégias que te ajudará a resolver diferentes desafios arquiteturais, aplicando soluções de forma elegante. São estratégias em que desenvolvedores de todo o mundo tem adotado em seus projetos. Imagina se você precisa atuar em um projeto internacional, caso este projetos tem adotado estratégias de Padrões de projeto, você não terá nenhuma grande dificuldade em entender a dinâmica e a arquitetura do código ou caso contrário, você mesmo poderá aplicar os conhecimentos adquiridos através deste livro. O mais interessante que Padrões de Projeto independem de linguagem de programação, é possível aplicar em qualquer outra linguagem, ou seja, é um conteúdo 100% reutilizável. Java Efetivo escrito por Joshua Bloch e traduzido para o português, é um livro excelente para aqueles que possuem um pouco mais de experiência com Java mas que precisam aprofundar em detalhes mais específicos da linguagem como, quando e como usar específicos Padrões de Projeto ou Design Patterns, utilização de Lambdas e Streams, concorrências e serializações. Para aqueles que possuem mais de experiência, é um excelente material de consulta. Junit em Ação é um famoso livro sobre testes que cobre técnicas de como efetuar testes práticos em seu código, utilizando técnicas de testes automatizados, testes unitários, testes de forma isolada e muito mais. É através da prática de testes que garantimos uma entrega de qualidade do software para o cliente final, evitando quebras na fase de Build e Deploy, entregando features íntegras e confiáveis de acordo os requisitos do projeto. Mastering Unit Testing Using Mockito and JUnit (Versão em Inglês) é um livro que cobre práticas de JUnit utilizando uma das bibliotecas de testes mais famosas chamada Mockito. Neste livro ensina-se a como criar e manter testes unitários automatizados utilizando features avançadas do JUnit junto ao framewok Mockito. Práticas de integrações contínuas (o famoso CI) utilizando ferramentas de mercado como Jenkins junto com um dos maiores gerenciadores de dependências em projetos Java, o Maven. Para você que está começando neste mundo é uma excelente escolha. Bakana, né? Já é uma opção para melhorar seus testes nas próximas aplicações.