DEV Community

Yuri Vecchi
Yuri Vecchi

Posted on

Como uma cultura de testes garante a qualidade de sua aplicação

Quantas não foram as vezes que após fazer o deploy de uma alteração, uma correção teve que ser feita às pressas, seja por um erro de programação, ou um cenário que não foi previsto durante o desenvolvimento, um erro pequeno que se outro programador tivesse a oportunidade de revisar não teria ido para produção.

A cultura de testes é pouco difundida nas empresas que trabalham com PHP no Brasil, levando a uma baixa qualidade de código e a estigmatização da linguagem como sendo de baixa qualidade. Porém, com a experiência, nós percebemos que a linguagem oferece os recursos necessários, e somos nós que não utilizamos todo o potencial oferecido, seja por desconhecimento ou preciosismo.

O PHP sofreu várias mudanças recentemente, desde performance até funcionalidades, tornando-se uma linguagem ainda mais amigável para o aprendizado, e com isso a barreira de entrada se tornou ainda mais baixa, e como consequência temos cada vez mais desenvolvedores iniciando sem ter uma boa base de formação, pulando etapas para chegar logo aos tão sonhados cinco dígitos de salário, e uma das etapas frequentemente ignorada é justamente a habilidade que vai garantir o seu emprego no final do mês: Os Testes.

Testes, simplesmente por existirem, não garantem que sua aplicação irá funcionar para sempre da maneira planejada, pelo contrário, irão dar uma falsa sensação de segurança a cada deploy realizado, e a única maneira de mitigar isso é amadurecendo toda uma cultura de testes de sua aplicação, sendo necessário que todos os desenvolvedores envolvidos no projeto estejam cientes e de acordo.

O elemento mais importante ao desenvolver essa cultura é a colaboração, com muita humildade, entre os desenvolvedores, através de code reviews bem elaborados por parte do revisor e aceitação por parte do revisado. Quando desenvolvemos uma solução temos que ter em mente que ela não é feita para nós, e sim para os outros, e isso acarreta diretamente na maneira como escrevemos nossos algoritmos. Conhecimentos profundos de SOLID, Design Patterns, Estrutura de dados, entre outros, ajudam nesse processo, pois servem de linguagem comum entre as partes no momento da discussão dos caminhos escolhidos. Lembrem-se, o revisor nem sempre vai conhecer a regra de negócio implementada no seu código, mas ao ler o mesmo, ele deve conseguir saber o que está acontecendo.

Com isso em mente, devemos sempre nos perguntar, eu consigo fazer uma alteração em minha aplicação sem mudar o comportamento esperado dela? Eu sei qual é o comportamento esperado dela? O próximo desenvolvedor que fizer alterações vai compreender o comportamento esperado? Felizmente temos como garantir esse comportamento através de testes, geralmente feitos de maneira manual e pessoal no começo do desenvolvimento, o Teste Exploratório, mas que logo devem ser complementados, se já não implementados desde o início do desenvolvimento, por testes mais assertivos, como os Testes Unitários seguidos pelos Testes de Integração.

Testes Unitários

São a menor parte da sua aplicação que pode ser testada, e é nessa parte que entram os conhecimentos de SOLID e Código Limpo, tornando os pedaços da sua aplicação o menor possível, assim diminuindo a Complexidade Ciclomática e tornando suas funções determinísticas, ou seja, sempre que receber a mesma entrada, vai retornar a mesma saída. Tomamos como exemplo a função abaixo, embora ela respeite muitos dos princípios de código limpo, ela não nos retorna sempre o mesmo valor, tornando assim o teste impossível.

Porém com uma pequena alteração, ela se torna testável e assim aumentamos a cobertura de nossos testes.

Perceba que se não passarmos o timestamp como argumento, nunca teremos a certeza do resultado.

Testes de Integração

São aplicados sobre serviços externos ao algoritmo desenvolvido, seja uma API externa aonde se busca uma cotação de uma moeda, ou um pacote dentro do repositório da própria empresa, porém sem acesso para alteração por parte da sua equipe, estes testes são mais complexos de se aplicar pois o resultado pode ser alterado a qualquer momento, e nós não temos controle sobre o código externo, tudo o que podemos fazer é nos preparar para o pior e torcer pelo melhor.

Mais uma vez temos uma função não determinística, porém desta vez não conseguimos simplesmente isolar o cliente de http, pois o resultado da requisição estará completamente fora do nosso controle.

Neste momento utilizamos Mocks, simulando o resultado das chamadas, para que possamos nos assegurar do retorno para o nosso código, a biblioteca do Guzzle já traz esta funcionalidade implementada para nós.

Code Review

Mesmo com os testes passando e a cobertura de código de 100%, ainda podem existir problemas de código mal escrito, regras de negócio não muito claras, entre outras singularidades. Para esses casos nada melhor do que confiar a revisão de nosso código para outro desenvolvedor, com outra visão sob as soluções implementadas e que igualmente respeite as boas práticas de programação. Neste momento é muito importante termos empatia, tanto como revisor, como revisado, saber que nem todos estão no mesmo momento de aprendizado e experiência, e que embora sejamos programadores, nenhum de nós é Deus, e tem onisciência para ditar o que é certo ou errado.

Como revisor é importante apontar os erros de maneira transparente e bem detalhada, sempre com paciência, pois muitas vezes o revisado pode pensar que está com a razão, e aquela solução encontrada é a única possível.

Já como revisado é necessário manter a mente aberta para outras ideias para a solução daquele problema, mesmo que signifique reescrever todo código do zero. Leve em consideração que o revisor não está fazendo aquilo por motivos pessoais, e sim porque ambos são responsáveis por manter a aplicação saudável para se trabalhar.

Conclusão

No EBANX essa cultura de testes é levada muito a sério, com seções de clean code semanais, aonde conseguimos aprimorar mais nossas técnicas, code reviews feitos por mais de um desenvolvedor, com discussões e troca de ideias sobre as soluções implementadas, uma base extensa de testes unitários, para garantir o funcionamento da aplicação, deploys automatizados, sempre executando todos os testes para ter a certeza de que todas as alterações irão funcionar quando combinadas e o mais importante de tudo, o respeito de todos os envolvidos pelo processo.

Uma cultura de testes vai muito além de delegar responsabilidades do funcionamento de seu código para outras pessoas, escrever funções impecáveis, ter 100% de cobertura de código ou estar preparado para um teste de caos e ter uma aplicação resiliente. Vai de aprender com os erros, aceitar novas ideias, evoluir como desenvolvedor e garantir uma boa noite de sono a todos os que dependem de sua aplicação rodando sem falhas. Isso é um pouco da experiência adquirida ao longo dos anos que cada um de vocês se identifica em partes ou completamente que fazem valer a pena continuar nessa profissão tão incrível e dinâmica que é a programação.

#TAMOJUNTO #EBANXLIFE #DEVSHIELD

Contribuidores: João Pedro L., Jorge Carboni, Leonardo Strozzi, Lucas Maldaner, Marcelino Janing e Yuri Vecchi

Fontes:

https://blog.cleancoder.com/uncle-bob/2014/11/15/WeRuleTheWorld.html

https://en.wikipedia.org/wiki/Cyclomatic_complexity

https://en.wikipedia.org/wiki/Deterministic_algorithm

https://grumpy-learning.com

https://martinfowler.com/articles/practical-test-pyramid.html

http://docs.guzzlephp.org/en/stable/testing.html

https://phpunit.de/getting-started/phpunit-9.html

https://en.wikipedia.org/wiki/Chaos_engineering

Top comments (1)

Collapse
 
hugobandeira profile image
Hugo Bandeira

Parabéns, ótima abordagem