Pirâmide de Testes

Teste de Software não é um conceito novo dentro da história da programação, mas foi em tempos mais recentes, a partir de estudos avançados que foram surgindo entre 1957 e 1979, que ele deixou de ser somente uma forma de garantir que o software funciona e tornou-se um processo de detecção de erros.

Para garantir projetos de software com qualidade, os testes são indispensáveis, e para falar mais sobre o assunto, hoje nosso convidados para o Blog é o Karan, que é desenvolvedor aqui na nata.house

Continue a leitura e confira! 

Com os avanços tecnológicos que tivemos desde a década de 90, a complexidade dos softwares desenvolvidos cresceram substancialmente, aumentando também a complexidade para garantir a qualidade e a confiabilidade. 

Diante das dificuldades encontradas para manter essas estruturas, surgiu a necessidade de estabelecer processos que servissem como garantia e maneiras de gerenciar a qualidade do software. 

O conceito vem sendo amadurecido, e hoje temos processos mais sofisticados, robustos, automatizados e que atendem a diversos modelos de desenvolvimento.

Em meio ao surgimento de diferentes formas de se testar um software, foi necessário também pensar em estratégias para execução desses testes. Não adianta termos técnicas bem definidas, sem saber a maneira adequada de implementá-las. E foi nesse cenário que surgiu o termo Pirâmide de Testes, para auxiliar na priorização e definição de um bom conjunto de testes.

Podemos entender o termo Pirâmide de Testes como uma metáfora visual que nos dá a ideia de como devemos implementar nossos testes, em termos de quantidade e prioridade em que devem ser feitos, sendo eles agrupados visualmente em uma estrutura de pirâmide, com diferentes níveis e granularidades.

Esse conceito foi criado por Mike Cohn, em seu livro “Succeeding with Agile”, e desde então vem sendo utilizado como base para modelos com diferentes camadas e abstrações, mas seguindo o mesmo princípio. 

O modelo proposto por Mike Cohn continua sendo referência para outras abordagens e tem sido fundamental para a definição de uma boa estratégia para os testes.

Neste conteúdo iremos abordar o modelo proposto por Mike Cohn, em que ele definiu três camadas para uma suíte de testes, sendo elas: Unit Tests, Service Tests e UI Tests. 

Na nossa abordagem utilizamos uma definição um pouco diferente para as camadas, mas que não altera a sua essência, e entenderemos essas camadas como Testes de Unidade,

Testes de Integração e Testes de Ponta a Ponta (ou E2E), respectivamente. 

A definição que escolhi não foi algo pensado simplesmente para esse material, mas algo pensado e consolidado na comunidade, e que traz um entendimento bem coerente acerca do que representam.

Testes de Unidade

Os testes de unidade correspondem à camada que está na base da pirâmide de testes e devem ser pensados como um pilar dessa estrutura. 

Essa é a camada em que teremos o maior número de testes e tem como objetivo isolar a menor parte testável de um software, eliminando qualquer interação externa real, garantindo que ela funcione de acordo com as suas especificações. 

Fazendo uma analogia a conceitos mais comuns, como Orientação a Objetos, seria como testar o método público de uma classe, garantido que a entrada e a saída estejam de acordo com o esperado. 

Métodos e atributos privados não são objetos de testes e normalmente entende-se que estão funcionando. Se uma classe possui muitos métodos privados, é uma boa indicação que essa classe está assumindo muitos papéis, ferindo o princípio de responsabilidade única (S do SOLID), e deve ser transformado em uma nova classe com o método público. 

Existem algumas técnicas que facilitam o isolamento das classes e podem ser utilizadas de acordo com o resultado desejado, como: fakes, mocks, spies, stubs e dummies.

Podemos pensar nos testes de unidade como a linha de frente na nossa estratégia de testes. Eles são os primeiros a serem implementados e cada unidade tem um papel fundamental na correção de erros e prevenção de falhas. 

Se bem implementados, também servem como guia para quem busca entender o funcionamento do projeto e para novos desenvolvedores que entram no projeto.

Esses testes tendem a ser menores, com menos complexidade, cobrem o maior número de linhas do código, possuem tempos de execução rápidos e são fáceis de identificar os erros.

Testes de Integração

Pense comigo no seguinte cenário: temos pneus de uma bicicleta, uma cabine de caminhão e uma suspensão de carro. Separados todos funcionam muito bem e estão de acordo com as suas especificações, porém, quando integrados, os pneus não encaixam na suspensão do carro e a cabine também não encaixa na suspensão. Não dá certo. 

É necessário organizar essa bagunça e é para resolver esse tipo de problema que os testes de integração são aplicados. 

Apesar de garantirmos que as unidades do software funcionam separadamente, é necessário também garantir que quando integradas elas mantenham o funcionamento conforme seus requisitos. Ou seja, precisamos garantir que as unidades funcionem em conjunto e com suas dependências.

Os testes de integração estão na camada central da pirâmide e, diferente dos testes de unidade, possuem um número menor de casos de teste, tendem a ser mais demorados, demandam um ambiente mais completo e dependem de interações externas, o que os tornam mais complexos de serem implementados e normalmente possuem um tempo maior de execução. 

Por conta da sua complexidade, uma boa estratégia para essa camada é validar cenários que não são possíveis serem cobertos com testes de unidade e não fazem muito sentido nos testes de ponta a ponta.

Identificar módulos que são sensíveis para o funcionamento do sistema e integrações com serviços externos é uma boa estratégia para identificar possíveis casos de teste para essa camada, abrangendo ainda mais a cobertura do código e minimizando a possibilidade de erros.

Eles são muitas vezes aplicados para validar a comunicação com bancos de dados, microsserviços, sistemas de arquivos, APIs externas e os controllers do próprio projeto.

Testes de Ponta a Ponta (E2E)

Os testes de ponta a ponta estão na camada mais alta da pirâmide e tem como objetivo testar todo um fluxo da aplicação, do início ao fim, buscando simular a rotina de um usuário dentro dessa aplicação e validar se está respondendo conforme os requisitos do projeto. 

Por serem mais complexos de dar manutenção, demandarem um ambiente mais robusto e possuírem um tempo de execução muitas vezes elevado e custoso, normalmente são aplicados para os fluxos que são mais utilizados pelos usuários, fluxos de rotinas sensíveis para o sistema, entre outras possibilidades, mas nunca em excesso. Sempre o mínimo possível. 

Temos sempre que ter em mente que estamos no topo da pirâmide e nesse ponto devemos ter o mínimo de casos de teste e somente os necessários. Isso porque são testes que demoram mais de serem implementados, não utilizam nenhum tipo de simulação de dados, possuem um custo elevado de manutenção, maiores chances de falhas e maior dificuldade na identificação dos erros. 

Além do mais, já estruturamos nossa base de testes de forma a cobrir uma grande parte das possibilidades de falhas e de erros, validando todas as unidades testáveis e as integrações mais importantes dentro do nosso projeto. 

A ideia dos testes de ponta a ponta é buscar falhas de integridade de dados e gargalos dentro de um determinado fluxo e que podem gerar problemas críticos. Por isso, é necessário um planejamento mais elaborado do que nas outras camadas e um entendimento mais profundo do projeto, para que seja possível mapear os cenários que são relevantes e que possuem o maior impacto dentro do sistema.

Concluindo, manter uma estrutura de testes consistente nunca será uma tarefa fácil, mas é fundamental para diminuir os erros e garantir a qualidade do software. 

É possível até dizer que um código sem testes automatizados é um código sem qualidade. 

É uma etapa extremamente importante e, se aplicada uma metodologia com estratégias bem definidas, teremos um processo mais fluido, em um ambiente de execução seguro e com resultados assertivos.

Uma boa suíte de testes deve respeitar seus níveis e granularidades, onde entendemos que quanto mais alto o nível da pirâmide, menos casos de teste teremos, serão mais lentos, com maior custo e maior será a complexidade em dar manutenção.

Nossa estratégia deve ser iniciada cobrindo a maior parte do código com testes de unidade na sua base, que são mais rápidos de serem implementados, mais leves, com tempos de execução baixos, e de fácil manutenção. Seguindo pelos testes de integração, que irão cobrir cenários críticos de integração no sistema e serão aplicados em uma quantidade inferior aos testes de unidade. 

Para completar a cobertura do nosso código, aplicamos os testes de ponta a ponta, para os fluxos mais importantes e utilizados pelos usuários, e que geram um impacto maior caso ocorra alguma falha.


Referências:

BARTIÉ, Alexandre. Garantia da Qualidade de Software, Rio de Janeiro: Campus, 2002

https://engsoftmoderna.info/cap8.html

https://www.devmedia.com.br/casos-de-teste-aprimore-seus-casos-e-procedimentos-de-teste/30526

https://medium.com/rd-shipit/test-doubles-mocks-stubs-fakes-spies-e-dummies-a5cdafcd0daf

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


A qualidade do software está diretamente ligada à sua usabilidade, segurança, pleno funcionamento, fácil manutenção e funcionalidades adequadas para o usuário a que se destina.

Para garantir essa qualidade, a etapa de testes de software é fundamental.

Sabemos disso, e é por isso que aqui na nata.house temos presente na nossa rotina de desenvolvimento os testes necessários para a melhor entrega possível, agregando alto valor as soluções que desenvolvemos. 

Quer saber mais sobre como a nata.house pode contribuir para o crescimento do seu negócio? Fale com um dos nossos especialistas! 

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *