2. Agenda
● Problema
● O que é teste de mutação?
● Exemplos
● Por que?
● Ferramentas
● Uso num projeto real
3. Problema
● Os testes estão corretos?
● Cobrem todos requisitos?
● Qual a qualidade dos testes?
4. Problema
● Cobertura de linhas apenas verifica execução de código
● 100% de cobertura != boa suite de testes
● Apenas testes "happy path" ou fáceis
● Código apenas parcialmente testado
6. Origem
● Proposto na Academia ~ 1978
● Yale University e Georgia Institute of technology
● Hipótese do programador competente
○ Programas quase perfeitos
○ Falhas pequenas
○ Pequenas alterações para correção
7. O que é teste de mutação?
● Introduz falhas (mutações) na implementação
○ Alterações semânticas
● Roda os testes
● Os testes realmente quebraram?
● Mutações mortas ou vivas?
● Os testes identificaram as falhas?
8. Exemplo
Mutação de troca de && para ||
if (a && b) {
c = 1;
} else {
c = 0;
}
if (a || b) {
c = 1;
} else {
c = 0;
}
9. Efeito colateral não testado
@Test
public void
shouldFailWhenGivenFalse() {
assertEquals("FAIL", foo(false));
}
@Test
public void
shouldBeOkWhenGivenTrue() {
assertEquals("OK", foo(true));
}
public static String foo(boolean b) {
if ( someThing(i) ) {
doImportantBusinessLogic();
return "OK";
}
return "FAIL";
}
Não verifica se doImportantBusinessLogic foi chamado
10. Teste de limite incompleto
@Test
public void
shouldReturnBarWhenGiven1() {
assertEquals("bar", foo(1));
}
@Test
public void
shouldReturnFooWhenGivenMinus1() {
assertEquals("foo", foo(-1));
}
public static String foo(int i) {
if ( i >= 0 ) {
return "foo";
} else {
return "bar";
}
}
O comportamento quando i==0 não foi testado
11. Mockista Míope
@Test
public void
shouldPerformActionGivenTrue() {
foo(mockCollab, true);
verify(mockCollab).doAction();
}
@Test
public void
shouldNotPerformActionGivenFalse() {
foo(mockCollab, false);
verify(never(),mockCollab).doAction();
}
public static String foo(Collaborator c,
boolean b) {
if ( b ) {
return c.doAction();
}
return "FOO";
}
Retorno da função nunca foi testado
12. Operadores de mutação
● Exclusão de declarações
● Duplicação ou inserção de declarações
● Negação de subexpressões booleanas
● Substituições:
○ Operadores aritiméticos (+, -, *, /)
○ Relações booleanas (==, !=, <, <=, >, >=)
○ Variáveis do mesmo escopo (tipos devem ser compatíveis)
Escore de mutação = № de mutantes mortos / № total de
mutantes
13. Por que?
● Ajuda a criar suítes de teste efetivas
● Propósito educacional
● Mostra quanto pode-se confiar numa suite de teste
○ Código legado
● Ajuda na refatoração e na criação de testes de
caracterização
● Verificar se alguma implementação está bem testada
14. O que não resolve?
● Caro para gerar e executar mutantes
● Limitado aos operadores de mutação
● Útil para teste unitários (doing things right)
● Ainda devem existir testes funcionais (doing the right
things)
15. Ferramentas
● PIT for Java
● Mutant for Ruby
● PyMuTester for Python