
Compartilhar:
Sou ator formado, com uma dissertação sobre stand-up comedy, e comecei a me dedicar ao desenvolvimento em PHP por meio dos encontros da comunidade. Você pode me encontrar dando palestras e escrevendo sobre tecnologia, ou ouvindo e comprando discos curiosos da minha coleção de vinil.
Otimize seu código PHP com os testes de mutação do PEST
PEST tem tido uma boa aceitação, especialmente desde que se tornou a biblioteca de testes padrão para o usuários do Laravel , oferecendo aos desenvolvedores do Laravel a opção de escolher entre PHPUnit ou o PEST, sendo que o PHPUnit é executado nos bastidores. Ter mais opções só pode ser algo positivo, mas as taxas de adoção também significam um ciclo de vida de desenvolvimento que passou por muitas novos recursos sendo lançados.
A rapidez com que os testes de mutação ganharam popularidade foi surpreendente, considerando que nada de significativo havia acontecido no mundo dos testes desde Domain-Driven Design e Desenvolvimento Orientado a Comportamento. Lembro-me de ter ficado impressionado com uma demonstração do BDD analisando automaticamente o arquivos Gherkin e gerando testes a partir deles, feita por Ciaran McNulty no PHP London há alguns anos. É algo que eu nunca fiz profissionalmente, mas quando os testes de mutação começaram a ser adotados pelos desenvolvedores de PHP após a popularidade do InfectionPHP, eu soube que seria algo em que eu veria grande valor.
Assim, com os testes de mutação se tornando cada vez mais populares, Nuno Maduro anunciou que o PEST 3.0 incluiria testes de mutação. Neste artigo, vamos nos aprofundar no assunto com um exemplo de teste usando a Messages API da Vonage.
O que é o teste de mutação e por que utilizá-lo?
Gostaria de começar dizendo que qualquer ferramenta que você possa adicionar para tornar seu código o mais robusto possível sempre será algo positivo. Portanto, você pode incluí-la na lista de itens essenciais, como a Análise Estática com ferramentas como PsalmPHP ou PHPStan, linting com algo como o phpcsfixer, além de testes robustos com Desenvolvimento Orientado a Testes.
Somos seres humanos e, como tal, cometemos erros em nossos testes (sei que já cometi). Há muitas coisas que podem dar errado — imagine que você esteja escrevendo um teste e ele não tenha nenhuma asserção; você só quer verificar se o código foi executado sem lançar uma exceção.
public function testService()
{
$service = new \Vonage\ServiceContainer('dev');
$service->runService();
// If service fails, exception, so dummy completion assertion
$this->assertTrue(true);
}O que você tem aí é um falso positivo. Claro, dá para entender o raciocínio por trás disso, mas você também tem
100% Line Test CoverageTecnicamente correto, mas isso não é um teste de verdade. E se o código subjacente mudar de tal forma que a exceção não documentada que poderia ser lançada não possa mais ocorrer?
O que os testes de mutação fazem é alterar o código subjacente, uma linha de código por vez (chamamos essas linhas de “mutantes”). Se você começar a modificar o código, tecnicamente, todos os seus testes deveriam falhar. Já introduzimos o elemento humano nisso, e sabemos que talvez não seja esse o caso. O resultado final que buscamos é que muitos, muitos mutantes tenham sido criados, mas que todos tenham sido “eliminados”, ou seja, que o teste tenha falhado por causa da mutação. Se você tiver mutantes que “sobrevivem”, seus testes ainda estarão sendo aprovados. Se eles ainda estiverem sendo aprovados, isso significa que os testes não perceberam nenhuma alteração no seu código. Isso não é bom.
Nossa referência: Teste do SDK PHP da Vonage
Vamos mostrar como implementamos um teste comum, usando como exemplo um serviço Laravel imaginário que envia uma mensagem RCS por meio do Vonage.
<?php
namespace App\Services;
use Vonage\Client;
use Vonage\Messages\Channel\RCS\RcsText;
class RcsService
{
public function __construct(
private Client $vonage
) {}
public function send(string $to, string $message): array
{
$response = $this->vonage->messages()->send(
new RcsText($to, 'VonageApp', $message)
);
return $response;
}
}Para simplificar, vamos supor que o contêiner de serviços do Laravel injeta um objeto Vonage Client pré-configurado com credenciais no construtor. Agora, precisamos de um teste para ele que não faça, de fato, uma chamada à API. Podemos fazer isso usando simulações (mocks).
use App\Services\RcsService;
use Vonage\Client;
use Vonage\Messages\Channel\RCS\RcsText;
use Vonage\Messages\Client as MessagesClient;
beforeEach(function () {
$this->messagesClient = Mockery::mock(MessagesClient::class);
$this->vonage = Mockery::mock(Client::class);
$this->vonage->shouldReceive('messages')->andReturn($this->messagesClient);
});
afterEach(function () {
Mockery::close();
});
test('Will send message using Vonage SDK to end number', function () {
$response = ['message_uuid' => 'abc-123', 'to' => '33600000000'];
$this->messagesClient
->shouldReceive('send')
->once()
->with(Mockery::type(RcsText::class))
->andReturn($response);
$service = new RcsService($this->vonage);
$result = $service->send('+33600000000', 'Hello world');
expect($result)->toBeArray();
});OK, bem, o teste vai ser aprovado. Ótimo, ótimo. Mas e se a gente alterar o código?
O teste de mutação está integrado ao PEST 3, o que significa que podemos executar mutações nele agora mesmo e ver o que acontece. Existem várias opções de configuração para o PEST, mas a maneira mais simples de fazer com que as mutações ocorram é usando o covers() . Nesse caso, a seguinte linha é adicionada ao arquivo de teste:
use App\Services\RcsService;
covers(RcsService::class);Isso indica ao PEST que, ao executar este arquivo de teste, ele deve mutar o código em RcsService. Para executar as mutações do PEST com um limite de aprovação de 100%:
./vendor/bin/pest --mutate --min=100Chegamos ao seguinte resultado:
Mutating application files...
1 Mutations for 1 Files created
RUN app/Services/RcsService.php
⨯ Line 20: AlwaysReturnEmptyArray
---------------------------------------------------------------------------------------------------------------------------
UNTESTED app/Services/RcsService.php > Line 20: AlwaysReturnEmptyArray - ID: 5befe24d3b8d7f6f
public function send(string $to, string $message): array
{
$response = $this->vonage->messages()->send(new RcsText($to, 'VonageApp', $message));
- return $response;
+ return [];
}
}
Mutations: 1 untested, 0 tested
Score: 0.00%
Duration: 0.30s
FAIL Mutation score below expected: 0.0 %. Minimum: 100.0 %.Opa, tem algo errado. Mas o que será? Aha! Veja só o incrível poder dos testes de mutação, explicados nas etapas a seguir:
O PEST modifica o
RcsService()para sempre retorne um array vazioO PEST executa o arquivo de teste como um mutante
Queremos que o mutante fracasse, e, assim, ser morto
Não é assim, isso passa
Isso significa que que seu teste é muito fraco
Então, por que isso é tão fraco? A conclusão é: você deve testar a carga útil retornada pela Vonage. Além do fato de que essa API da Vonage nunca retorna, na verdade, um array vazio, você deve testar a carga útil que é retornada. Usando ->toBeArray() neste caso não é suficiente.
Como superar seus pontos fracos
Na verdade, o teste define a saída simulada que é fornecida; portanto, é seguro presumir que o que estamos testando aqui é se a carga útil retornada (que nós mesmos fornecemos) não sofreu nenhuma alteração. Como definimos $response, devemos alterar o teste para garantir que ele corresponda exatamente a essa estrutura de dados.
expect($result)->toBe($response);É isso aí. Uma linha, basta alterar essa asserção. Ao executar as mutações do PEST novamente, obtemos o seguinte:
Tests: 1 passed (2 assertions)
Duration: 0.26s
Mutating application files...
1 Mutations for 1 Files created
RUN app/Services/RcsService.php
✓ Line 20: AlwaysReturnEmptyArray
Mutations: 1 tested
Score: 100.00%
Duration: 0.34sUau.
Conclusão
Sei que, como ponto de partida, este é um exemplo bastante básico. No entanto, o conceito por trás do que o teste de mutação oferece é basicamente o que estamos abordando aqui. Apesar de serem apenas algumas linhas de código, ele demonstra o enorme potencial que os testes de mutação têm para fortalecer sua base de código. Considere usá-los em conjunto com algo como o PHPStan, e os desenvolvedores de PHP nunca tiveram condições melhores para entregar código à prova de falhas.
Tem alguma dúvida ou quer compartilhar o que está criando?
Inscreva-se no Boletim Informativo para Desenvolvedores
Siga-nos no X (antigo Twitter) para ficar por dentro das novidades
Assista aos tutoriais no nosso canal do YouTube
Conecte-se conosco na página de desenvolvedores da Vonage no LinkedIn
Fique conectado e acompanhe as últimas notícias, dicas e eventos para desenvolvedores.
Compartilhar:
Sou ator formado, com uma dissertação sobre stand-up comedy, e comecei a me dedicar ao desenvolvimento em PHP por meio dos encontros da comunidade. Você pode me encontrar dando palestras e escrevendo sobre tecnologia, ou ouvindo e comprando discos curiosos da minha coleção de vinil.