Usando PHPUnit em Continuous Integration

Guilherme Brito
Printi Tech
Published in
5 min readFeb 18, 2022

Você já deve ter visto muitos artigos técnicos que se propõe a pincelar ou até mesmo abordar mais profundamente os conceitos de testes unitários usando o phpUnit, certo?! Porém essa não é a intenção deste artigo.

Pretendo, de maneira narrativa, contar nossa experiência implantando uma rotina de testes automatizados em um ambiente ágil que já tinha uma rotina de deploy estabelecida. Isso mesmo, pretendo descrever nossa jornada nessa empreitada com a intenção de ajudar quem está passando por isso ou apenas pensa que seria bom ter isso implantado em seu projeto.

Há muito tempo vinha-se falando que precisávamos ter uma pipeline de testes automatizados na empresa, mas nunca era priorizado (esse cenário é familiar pra você também?). Quando finalmente decidimos dar o primeiro passo, me foi dada a missão de ser a primeira cobaia a escrever testes.

Mas por que PHPUnit?

Como era um projeto em andamento, já tínhamos uma arquitetura definida de micro serviços onde cada um tem o mínimo de responsabilidades possíveis (pelo menos nós achávamos isso) para ser de fato, micro. Para isso também tínhamos um skeleton, que tornava mais ágil a tarefa de criação de um novo micro serviço, usando as estruturas pré-definidas pelo time. Logo, nada mais óbvio do que o phpUnit, que já provia uma infinidade de funcionalidades, integrações, e, principalmente, definia o código de testes no mesmo repositório que o código, tornando mais fácil para o desenvolvedor dar manutenção. Sem falar que, quando se trata de PHP, a comunidade de usuários de phpUnit é sem dúvida muito forte.

No início

Há muito tempo atrás, em uma galáxia distante, começamos a analisar o que poderia ser otimizado em relação a testes. Percebemos que em algum momento, alguém teve a intenção de usar testes automatizados pois já haviam alguns testes escritos no próprio phpunit. Mas então, por que parou? Conversei com alguns desenvolvedores aqui e todos ficaram surpresos ao saber que já haviam alguns testes escritos. Foi então que eu percebi: Não havíamos continuado com essa iniciativa simplesmente porque ninguém sabia que tinha que dar manutenção e rodar. Logo precisaríamos de alguma ferramenta para pelo menos rodar automaticamente a cada commit, validando assim aquele novo pedaço de código com os testes que houvessem.

Integração contínua

Se eu puder dar um conselho de ouro: quanto antes você implementar uma pipeline de continuous integration melhor, ao meu ver, sem ela a cultura de manutenção de cobertura e de cenários de testes não funciona.

Tendo feita essa descoberta, passamos a criar nossa pipeline pós-commit com tudo que julgamos mais importante:

  • build
  • testes (phpUnit)
  • cobertura de código (phpUnit)
  • code-standard (phpcs)
  • code-analysis (sonarQube)

A princípio, usamos o bitbucket-pipeline. Funcionou bem, mas um pouco depois trocamos para o drone-ci, que apesar de não ser cloud e depender de infra própria, pra nós fez mais sentido na época. Não vou citar os prós e contras de cada ferramenta, pois não é a intenção desse artigo, mas vou deixar um link para aqueles que ficaram curiosos:

Pipelines:

Code quality:

Testes funcionais

Como eu disse, já tínhamos uma rotina de desenvolvimento e até mesmo de deploy estabelecida, fazíamos daily e várias outras cerimônias do scrum, mas mesmo assim, sentíamos dificuldade em reproduzir alguns cenários específicos de forma ágil e consistente. Foi aí que surgiu a ideia de cobrir alguns cenários usando o phpUnit, mas com dados em um banco de teste e “mockando” apenas o que fosse serviço externo. Poderíamos dessa forma, criar testes para nossos importadores, métodos de transição de status e outras coisas que víssemos valor em testar. Lembrando que nossos serviços eram basicamente APIs então os testes funcionais requisitavam uma rota e esperavam uma certa resposta.

É claro que sempre há alguma desvantagem, escrever teste funcional não é a melhor forma de garantir cobertura. Não porque a ferramenta não oferece suporte, ela oferece e muito, mas porque o teste funcional não tem foco em cobertura e sim na funcionalidade criada.

Testes Unitários

Testes unitários sendo úteis.

Na época nossos esforços acabaram sendo na maioria apenas focados em testes funcionais. Isso porque o teste é uma skill que você vai adquirindo conforme vai escrevendo, não é fácil escrever uma funcionalidade pensando em como vai ser testada, você só adquire essa visão depois de muita chuva nas costas. Ou seja, muito código legado torna muito trabalhoso de ser testado, exigindo longas refatorações e o nosso precioso tempo.

Isso recentemente gerou algumas discussões aqui, mas sem dúvida na época foi uma excelente escolha para começarmos a ter melhor qualidade de código com menos bugs sem gastar horas excessivas refatorando o código.

Não estou dizendo que o teste funcional possui mais valor que o teste unitário, pois os dois são coisas diferentes: um complementa o outro. Mas com certeza é necessário investir muitas horas refatorando para poder ter testes unitários, logo chegamos no conselho de ouro número 2: é melhor começar a escrever testes funcionais do que não começar a escrever.

Cobertura de código é importante?

A cobertura de código é um dos marcadores que nos sinalizam a quantidade de código que estão sendo cobertos por teste. Porém, não querendo ser repetitivo, mas já sendo: sempre dê preferência por começar a testar e fazer disso parte da rotina, a cobertura é consequência.

Amadurecer leva tempo

Como tudo na vida, ter uma boa rotina de testes leva tempo, mas não desanime Jovem Padawan. Só por estar querendo saber mais e buscar melhorar suas práticas você já está crescendo profissionalmente.

Para concluir leve contigo essa lição: escreva testes, erre escrevendo, aprenda escrevendo e desenvolva a habilidade de escrever cada vez mais códigos testáveis assim com certeza terá sucesso em sua jornada. E que a força esteja com você.

--

--