Melhorando a performance dos seus testes em PHP/Laravel
Tags: php, laravel, testing, braziliandevs
Published: 2021-05-06 21:49:29

# 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](https://github.com/paratestphp/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:
```bash
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:
```bash
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](https://github.com/paratestphp/paratest) 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.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x82hzvc6klhqb8ectg9f.png)

##### 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.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qoq01jkggezu5shb0ulh.png)

# 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
- Paratest: https://github.com/paratestphp/paratest
- Laravel Doc: https://laravel.com/docs/8.x/testing#running-tests-in-parallel