Testes de mutação com Infection
Avalie o quanto seus testes são realmente bons.
Testes de mutação com Infection é uma aula grátis de PHP Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de PHP Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de PHP Academy inclui 4 aulas no total.
A cobertura engana
A cobertura de 100% das linhas parece reconfortante, mas apenas comprova que seus testes executaram o código — não que eles detectariam um erro nele. Um teste pode executar uma linha sem verificar nada relevante. Os testes de mutação medem o que realmente importa: seus testes falhariam se o código tivesse um pequeno defeito? O Infection é a ferramenta padrão do PHP para isso.
composer require --dev infection/infectionA ideia central: mutantes
O Infection pega o código coberto e introduz pequenas falhas — mutantes. Ele troca > por >=, + por -, && por ||, remove um return e assim por diante. Em seguida, executa novamente seus testes contra cada mutante:
- Se um teste falhar → o mutante será eliminado (ótimo — seus testes detectaram a falha).
- Se todos os testes passarem → o mutante sobreviveu (ruim — um erro real nesse ponto passaria despercebido).
Um sobrevivente em ação
Considere esta função e um teste fraco. O Infection transformaria >= em >. Se nenhum teste verificar o limite exato (quando o valor é igual ao limite), esse mutante sobreviverá — revelando um caso extremo não testado.
<?php
function qualifiesForFreeShipping(float $total): bool
{
return $total >= 50.0; // Infection mutates >= to >
}
// Weak test only checks 100 and 10 -> never tests exactly 50.0
var_dump(qualifiesForFreeShipping(100.0)); // true
var_dump(qualifiesForFreeShipping(10.0)); // false
var_dump(qualifiesForFreeShipping(50.0)); // true <-- the boundary the mutant exposes
Eliminando o mutante
Adicione a verificação do limite e o mutante de >= para > morrerá: no código mutado, 50.0 > 50.0 resulta em false, portanto o teste falha — exatamente o que queremos. Os testes de mutação informam literalmente quais verificações estão faltando.
<?php
use PHPUnit\Framework\TestCase;
final class ShippingTest extends TestCase
{
public function test_threshold_is_inclusive(): void
{
// Kills the >= -> > mutant
self::assertTrue(qualifiesForFreeShipping(50.0));
}
}
Configuração: infection.json
O Infection é controlado por infection.json5. Você declara quais diretórios devem ser mutados, onde os registros serão armazenados e os limiares mínimos de pontuação para a integração contínua ser aprovada. Aponte source.directories apenas para o seu código de produção — nunca para os seus testes.
{
"source": {
"directories": ["src"]
},
"logs": {
"text": "build/infection.log",
"html": "build/infection.html"
},
"mutators": {
"@default": true
},
"minMsi": 80,
"minCoveredMsi": 90
}As métricas de MSI
O Infection informa o Indicador de pontuação de mutação:
- MSI = eliminados / total de mutantes. É penalizado pelo código não coberto (esses mutantes não podem ser eliminados).
- MSI da cobertura = eliminados / mutantes em linhas cobertas. Mede a qualidade das suas asserções nas partes que você testa.
- Cobertura de código por mutação = quanto código o Infection conseguiu mutar.
Uma cobertura de linhas alta, mas um MSI da cobertura baixo, é o sinal clássico de testes com poucas asserções.
Executando o Infection com eficiência
Os testes de mutação são custosos — a suíte é executada novamente uma vez para cada mutante. Duas grandes otimizações de velocidade são: executar os testes em paralelo com --threads e mutar apenas o código alterado pelo ramo atual usando a filtragem das diferenças do Git, o que é ideal para a integração contínua em solicitações de mesclagem.
vendor/bin/infection --threads=max --git-diff-lines --git-diff-base=origin/mainLendo os sobreviventes
O valor está nas diferenças que o Infection exibe para cada mutante sobrevivente. Elas mostram a linha exata e a alteração que seus testes não conseguiram detectar. Trate os sobreviventes como uma lista de tarefas: adicione a verificação que falta ou reconheça que o mutante é inofensivo (um mutante equivalente).
- return $total >= 50.0;
+ return $total > 50.0;
# Mutant survived: no test asserts the inclusive boundary (total === 50.0)Mutantes equivalentes e como ignorá-los
Alguns mutantes são equivalentes — alteram o código, mas não o seu comportamento observável, portanto nenhum teste poderia eliminá-los. Eles não podem ser eliminados e reduzem o MSI injustamente. Suprima mutadores conhecidos por gerar falsos positivos em trechos específicos do código, em vez de manipular os testes para persegui-los.
{
"mutators": {
"@default": true,
"Plus": {
"ignore": ["App\\Math\\Statistics::variance"]
}
}
}Onde os testes de mutação compensam
Como são lentos, escolha bem onde aplicá-los:
- Aplique-os à lógica central do domínio — definição de preços, permissões e cálculos — onde erros silenciosos são dispendiosos.
- Condicione as solicitações de mesclagem ao MSI da cobertura das linhas alteradas, não ao repositório inteiro.
- Não busque 100% globalmente; os ganhos decrescentes e os mutantes equivalentes tornam isso um desperdício.
- Use-os para encontrar asserções fracas e então corrija os testes — a pontuação é um meio, não o objetivo.
É preciso ter dados de cobertura para ganhar velocidade
O Infection só muta as linhas que seus testes realmente cobrem, portanto reutiliza os dados de cobertura do executor de testes. Com o Xdebug, isso é lento; o pcov é muito mais rápido para a cobertura de linhas e é o mecanismo recomendado para executar testes de mutação. O Infection também pode gerar a cobertura por conta própria ou usar um relatório de cobertura que você já tenha produzido na integração contínua.
# Faster mutation runs: use pcov instead of Xdebug for coverage
php -d pcov.enabled=1 vendor/bin/infection --threads=max
# Or reuse coverage already generated by your PHPUnit step:
vendor/bin/infection --coverage=build/coverage --skip-initial-testsVerificação rápida
O que um mutante sobrevivente informa?
Recapitulação
Você aprendeu a medir a qualidade dos testes, não apenas a quantidade:
- Os testes de mutação injetam pequenas falhas (mutantes); eliminado = detectado, sobrevivente = uma lacuna.
- A cobertura mostra a execução; o MSI da cobertura mostra a força das asserções.
- Configure por meio de
infection.json5com limites de MSI; execute com--threadse filtragem pelas diferenças do Git para ganhar velocidade. - Os sobreviventes são uma lista de tarefas; fique atento aos mutantes equivalentes e ignore-os deliberadamente.
- Priorize a lógica central e condicione as solicitações de mesclagem ao MSI das linhas alteradas, em vez de buscar 100% globalmente.
Perguntas Frequentes
A aula “Testes de mutação com Infection” é grátis?
Sim — o texto completo de “Testes de mutação com Infection” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de PHP Academy, atualize para CoddyKit PRO. O curso de PHP Academy inclui 4 aulas no total.
O que vou aprender em “Testes de mutação com Infection”?
Avalie o quanto seus testes são realmente bons. Você pratica PHP Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar PHP Academy?
Nenhuma experiência prévia é necessária. PHP Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Testes de mutação com Infection”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de PHP Academy?
Sim. Cada aula de PHP Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- O fluxo de trabalho do desenvolvimento orientado por testes
- Simulação e criação de dublês com Mockery
- Testes de integração e funcionais
- Testes de mutação com Infection