Melhorando a performance dos seus testes em PHP/Laravel
Pedro Paulo Silva
Posted on May 6, 2021
O problema
Você já se encontrou na situação em que tinha que fazer um deploy ou subir sua atualização urgente e teve que ficar esperando os testes terminarem para finalizar pipeline? Nessa hora qualquer minuto é uma eternidade.
Eu estava enfrentando esse problema, meus testes demoravam cerca de 6 minutos para rodar e esse tempo seguia aumentando a cada nova feature entregue.
A solução
Para resolver isso encontrei o pacote Paratest, basicamente, esse pacote adiciona suporte a testes paralelos ao PHPUnit, fazendo com que seus testes rodem simultaneamente.
Instalação
Primeiro começamos instalando o pacote:
composer require --dev brianium/paratest
Após isso, sua aplicação já tem suporte aos testes em paralelo. 🥳
Para começar a usar vamos rodar o comando:
vendor/bin/paratest -p4 --runner=WrapperRunner
Opções usadas:
-p4
A opção
-p
é referente ao número de processos simultâneos que irão rodar os testes.--runner=WrapperRunner
Por padrão, o PHPUnit inicia um novo processo para cada arquivo de testes que irá rodar, utilizando o
--runner=WrapperRunner
cada processo-p
irá iniciar apenas uma vez e manter tudo o que é preciso para rodar todos os arquivos de testes.Para saber todas as opções disponíveis e ter mais detalhes sobre as usadas aqui, acesse a documentação do pacote.
Resultados
Para efeitos de comparação vamos rodar modo sequencial, apenas com o PHPUnit e no modo paralelo, com o Paratest. Serão 646 testes com 75442 asserções, a maioria desses testes sendo de integração, com acesso ao banco de dados e outras dependências.
Testes Sequenciais:
O tempo levado pelo PHPUnit para rodar todos esses testes foi de aproximadamente 5 minutos e 39 segundos. Agora, lembre-se da situação em que você está esperando a pipeline terminar para finalmente fazer um deploy, esses quase 6 minutos se tornam uma eternidade.
Testes Paralelos:
Já o tempo levado pelo Paratest foi de aproximadamente 1 minuto e 11 segundos. Um tempo muito melhor para você que está ansioso esperando.
Nem tudo são flores 😓
Quando estamos trabalhando com paralelismo um grande problema é controlar o acesso de cada processo aos recursos e dados do sistema, e nesse caso não é diferente. Em testes que devem acessar o banco de dados, modificar e consultar registros pode ocorrer de dois testes acessarem o mesmo dado simultaneamente causando possíveis deadlocks no banco de dados e assim, também, um desses testes acabará falhando.
Uma possível solução para esses casos é o isolamento de cada teste no acesso ao banco de dados. É possível fazer isso rodando os testes dentro de uma transaction assim fazendo com que todos os registros modificados e/ou consultados estejam isolados e tudo que foi feito pelo teste é desconsiderado após seu término.
- O Laravel disponibiliza a Trait DatabaseTransactions, com ela no seu TestCase (classe na qual seus testes irão extender), seus testes irão por padrão rodar dentro de uma transaction.
- Caso use SQLServer, outra possível solução é habilitar a opção READ_COMMITTED_SNAPSHOT, ela ajuda com esses casos de dados sendo acessados e modificados simultanemente. Para mais detalhes acesse: https://www.mssqltips.com/sqlservertip/6368/sql-server-readcommittedsnapshot-database-option-and-read-commited-transaction-isolation-level/
- Observação: no meu caso as duas opções foram usadas.
Conclusão
Com a necessidade de cobrir sua aplicação com testes aumentando a cada dia, rapidamente você começa a se sentir incomodado com a demora na finalização dos testes. Para ajudar nesse ponto, a opção de rodar seus testes em paralelo é muito bem vinda, ela está aí para aumentar a performance e produtividade do seu time nos deploys e no dia a dia.
Se você ainda não usou, deveria dar uma chance e ver o tempo que você está perdendo (literalmente rs).
Fontes
Posted on May 6, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.