Teste de Mutação em PHP: Como escrever testes que realmente encontram bugs

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Por que 100% de cobertura de código é uma ilusão?

\n

Você já se orgulhou de um relatório do PHPUnit mostrando "Code Coverage: 98%"? E depois, ao lançar uma atualização, um bug crítico apareceu em produção? Parece familiar? O problema é que a cobertura clássica (line/statement coverage) apenas mede quais linhas de código foram executadas. Ela não verifica se seus testes realmente validam a lógica.

\n

Imagine que você escreveu um teste que verifica se uma função retorna true. Mas a função sempre retorna true, mesmo se estiver quebrada. O teste passa, a cobertura é de 100%, mas o bug não é detectado. É aqui que o teste de mutação em PHP entra em cena — uma técnica que transforma a forma como você enxerga os testes.

\n

Neste artigo, vamos explorar o que é o teste de mutação, como ele funciona com código PHP real e como implementá-lo no seu projeto usando a biblioteca Infection.

\n\n

O que é Teste de Mutação? (E não é mágica)

\n

Teste de mutação (mutation testing) é um método para avaliar a qualidade dos testes, onde pequenas alterações intencionais (mutações) são introduzidas no código-fonte. Se, após a alteração, o teste falha — o mutante foi morto, o teste funciona. Se o teste continua passando — o mutante sobreviveu, indicando que seu teste não está validando nada.

\n

Uma analogia simples: você é um segurança, e seus testes são câmeras. O teste de mutação envia "invasores" (mutantes) para o prédio. Se a câmera (teste) detecta o invasor, ela funciona. Se o invasor circula livremente, a câmera é inútil.

\n

Em PHP, a principal ferramenta para isso é o Infection — uma biblioteca que executa o PHPUnit em cada mutante e analisa os resultados.

\n\n

Como funciona na prática?

\n

Suponha que você tenha uma classe simples de calculadora:

\n
<?php\nclass Calculator\n{\n    public function add(int $a, int $b): int\n    {\n        return $a + $b;\n    }\n}
\n

E um teste para ela:

\n
<?php\nuse PHPUnit\\Framework\\TestCase;\n\nclass CalculatorTest extends TestCase\n{\n    public function testAdd()\n    {\n        $calc = new Calculator();\n        $result = $calc->add(2, 3);\n        $this->assertEquals(5, $result);\n    }\n}
\n

A ferramenta Infection cria um mutante, substituindo + por -:

\n
return $a - $b;
\n

Em seguida, executa o teste. Se o teste falhar — o mutante foi morto. Se o teste passar (e ele passará, porque você só testou 2+3, não 2-3) — o mutante sobreviveu. Isso é um sinal: seu teste não é bom o suficiente.

\n\n

Instalação do Infection e primeiro teste

\n

Para começar, instale o Infection via Composer:

\n
composer require --dev infection/infection
\n

Depois, crie um arquivo de configuração infection.json (ou execute vendor/bin/infection para configuração interativa).

\n

Exemplo de configuração mínima:

\n
{\n    "source": {\n        "directories": [\n            "src"\n        ]\n    },\n    "phpUnit": {\n        "configDir": "."\n    },\n    "mutators": {\n        "@default": true\n    }\n}
\n

Execute o teste de mutação:

\n
vendor/bin/infection --min-msi=100
\n

Onde --min-msi=100 significa que queremos que 100% dos mutantes sejam mortos (um ideal, raramente alcançável). O Infection gera um relatório em HTML (pasta infection.html), onde os mutantes sobreviventes são marcados em vermelho.

\n\n

Tipos de mutações em PHP (com exemplos)

\n

O Infection suporta dezenas de mutadores. Aqui estão os mais comuns que você encontrará:

\n\n

1. Operadores matemáticos

\n

Substituição de + por -, * por /, % por *, etc.

\n
// Código original\nreturn $price * $quantity;\n\n// Mutante\nreturn $price / $quantity;
\n

Blogs

Book Recommendations