O documento discute como a técnica de teste de mutação pode ser usada para testar a qualidade dos testes unitários, introduzindo deliberadamente erros no código (mutações) para verificar se os testes unitários identificam esses erros. Ele explica como a ferramenta MuJava implementa mutações em código Java e como analisar os resultados para melhorar a cobertura e eficácia dos testes unitários.
DevQA - Da zona de conforto ao comprometimento com a qualidade
DevQA: Como testar os testes unitários?
1. DevQA: Como testar os testes unitários? - por Kamilla Queiróz
Já testou os seus testes unitários? Como saber se você escreveu corretamente os testes
unitários? Isso pode ser mais uma tarefa para um DevQA!
Ferramentas de qualidade de código, testes unitários, testes de integração, são vários os
tipos de testes, mas como saber a qualidade deles? Como identificar se os testes unitários
estão sendo efetivos? As ferramentas de cobertura são boas alternativas para saber se os
testes estão utilizando uma certa linha de código, mas caso as variáveis sejam diferentes ou
algumas condições mudem, como manter essa garantia?
Quando é codificado um teste unitário, a preocupação é testar o resultado que está sendo
esperado, mas havendo várias possibilidades de fluxos, como vamos garantir que o teste
passou por todos os caminhos e não somente o happy day, ou seja, o caminho feliz ?
Para esse tipo de abordagem utilizando uma técnica chamada Mutation Testing, onde é
alterado o código original para introduzir falhas simples e verificamos se os testes unitários
irão quebrar.
Um dos métodos mais comuns para isso é o Bebuging, é isso mesmo, a escrita está correta. Ao
invés de 'debugarmos' o código, ou seja, depurarmos procurando bugs, erros ou falhas, iremos
introduzir erros na aplicação para testar a qualidade do teste unitário.
Essas falhas ou erros que introduzimos no código, chamamos de Mutação. Cada linguagem tem
uma estrutura e assim em linguagens orientadas a objeto temos os seguintes tipos de mutação
possíveis:
• Intra-method: dentro de métodos;
• Inter-method: entre métodos;
• Intra-class: entre métodos de uma classe;
• Inter-class: entre métodos de classes diferentes;
Temos uma variedade de operadores de mutação resultantes desses tipos de mutações
possíveis. Abaixo, vamos analisar uma ferramenta que implementa alguns desses operadores.
Para Java temos a ferramenta MuJava. Vejamos como essa ferramenta trabalha com um
exemplo:
2. Nesse exemplo da imagem, temos uma mutação na linha 28 do código, afim de induzir ao
erro. Introduzimos uma mutação de operador aritmético.
O programa original sofreu alterações, ou seja, aplicamos um operador de mutação. Se
executarmos os mesmos casos de testes originais e os testes unitários tiverem uma boa
qualidade, os testes unitários quebraram.
Caso isso não ocorra, temos o que o autor da ferramenta chama de mutações vivas, ou seja,
precisamos adicionar mais testes unitários pra que possamos "matar" essa mutação, e os
testes unitários quebrem, caso o programa original seja alterado.
Em resumo, os testes de mutação tem a capacidade de revelar o quão adequados estão os
testes unitários ou seus dados de teste.
Com esses resultados em mãos, podemos realizar uma análise de mutantes, que possibilita
calcularmos uma média objetiva de confiança dos casos de
o cálculo em mais detalhes no
Nesse exemplo da imagem, temos uma mutação na linha 28 do código, afim de induzir ao
tação de operador aritmético.
O programa original sofreu alterações, ou seja, aplicamos um operador de mutação. Se
executarmos os mesmos casos de testes originais e os testes unitários tiverem uma boa
qualidade, os testes unitários quebraram.
ocorra, temos o que o autor da ferramenta chama de mutações vivas, ou seja,
precisamos adicionar mais testes unitários pra que possamos "matar" essa mutação, e os
testes unitários quebrem, caso o programa original seja alterado.
tação tem a capacidade de revelar o quão adequados estão os
testes unitários ou seus dados de teste.
Com esses resultados em mãos, podemos realizar uma análise de mutantes, que possibilita
calcularmos uma média objetiva de confiança dos casos de testes unitários executados. Veja
o cálculo em mais detalhes no artigo aqui.
Nesse exemplo da imagem, temos uma mutação na linha 28 do código, afim de induzir ao
O programa original sofreu alterações, ou seja, aplicamos um operador de mutação. Se
executarmos os mesmos casos de testes originais e os testes unitários tiverem uma boa
ocorra, temos o que o autor da ferramenta chama de mutações vivas, ou seja,
precisamos adicionar mais testes unitários pra que possamos "matar" essa mutação, e os
tação tem a capacidade de revelar o quão adequados estão os
Com esses resultados em mãos, podemos realizar uma análise de mutantes, que possibilita
testes unitários executados. Veja
3. Tipos de mutações que podemos fazer no código:
• Exclusões de declarações;
• Duplicação ou inserção de declarações;
• Negação de sub-expressões booleanas;
• Substituições:
- operadores aritméticos [+, -, *, / ];
- relações booleanas [==, !=, <, <=, >, >=];
- variáveis do mesmo escopo [tipos devem ser compatíveis];
Problemas
Um dos problemas encontrados com essa abordagem é questão do tempo de execução dos
testes. Pois iremos incluir alterações diversas no código, assim para cada alteração
precisamos rodar todos os testes novamente, e com isso, o tempo de execução dos testes se
eleva bastante.
Uma solução para mitigar esse tempo, é a escolha correta dos operadores e aplicarmos nas
classes de maior interesse.
Em resumo:
• Testar os testes ajuda na criação de suítes efetivas;
• Apresenta um propósito educacional;
• Mostra o quanto pode ser confiável uma suíte de testes;
• Valida se alguma implementação está realmente bem testada.
Links Externos
MuJava
Artigo: MuJava : An Automated Class Mutation System
Publicado originalmente em O Tapioca: http://www.otapioca.com.br/?p=2239