Testes de Software
Testes de software devem ser considerados uma etapa crucial no processo de desenvolvimento de software. É interessante que percebamos o processo de criação e execução de testes como uma economia do tempo (e dinheiro) futuro, já que os testes podem levantar cenários para o qual nosso código não estava preparado, e já nos guiar para evitar falhas lá na frente.
Tendo em mente que alguns dos maiores objetivos em testar uma aplicação são garantir que o funcionamento dela está correto e se está atendendo aos requisitos especificados, o quanto antes o código for testado, menores serão os danos, ou até falhas que podem vir a atingir os usuários.
Boas práticas ao criar testes
Há uma série de boas práticas e convenções que podem nos auxiliar no momento de desenvolver um teste efetivo.
Estrutura
Com relação à estrutura de organização lógica do teste, uma convenção comumente praticada é a AAA:
ARRANGE (organize)
Aqui são organizados os dados necessários envolvidos na execução do teste, sejam dados exclusivos do contexto do teste, ou de uma camada externa.
ACT (aja)
Nessa etapa intermediária é checado o comportamento do trecho de código a ser testado, de forma que ele é executado.
ASSERT (confira)
Aqui, por fim, é feita a conferência da saída da execução do código na etapa anterior com a saída esperada para o determinado contexto do teste.
Adequação
Para auxiliar na checagem se o teste está de acordo com boas práticas, há também um padrão conhecido como acrônimo CORRECT:
- Conformity (conformidade): Os dados retornados após a execução do teste são do tipo esperado
- Order (ordem): A ordem dos resultados é retornada da maneira esperada
- Range (variedade): Validação do tamanho do dado
- Reference (referência): Checa se o teste faz referência a um trecho de código externo
- Existence (existência): Checa se um resultado vazio, nulo ou negativo cabe ao resultado esperado
- Cardinality (cardinalidade): Checa se o resultado retornado é suficiente ou se pode ser mais completo
- Time (tempo): Checa se as etapas dentro do contexto do teste estão ocorrendo na forma correta, o mais fiel possível à realidade de sua execução
Nomenclatura de testes
Com relação ao momento de nomeação de um teste, há algumas práticas mais conhecidas:
- “Should-When”: Certo acontecimento deve ser gerado após a ação indicada.
Por exemplo: Deve aplicar desconto ao adicionar cupom();
- “Given-When-Then”: Dado um contexto, quando acontecer certa ocorrência, deve ser gerado um determinado resultado.
Por exemplo: Dado carrinho fechado quando pagar deve confirmar compra();
- “When-Then”: Mesmo formato do “Given-When-Then”, porém sem a especificação do contexto.
Por exemplo: Quando compra for finalizada deve exibir previsão de entrega();
Abrangências de Testes
Existem vários tipos de teste, vamos trazer aqui um pouco sobre tipos de testes de acordo com sua abrangência, nos baseando na Pirâmide de Testes como ilustrado na figura abaixo.
Testes de ponta a ponta
Já no topo da pirâmide vemos os testes de ponta a ponta (end to end), exibido na figura com uma menor proporção e a menor velocidade da pirâmide. Essa proporção vai sendo aumentada conforme descemos a pirâmide, de forma que o maior quantitativo de testes deve estar localizado na base da pirâmide, e o menor, no topo.
Os testes de ponta a ponta, conforme o nome já nos indica, devem testar o funcionamento da aplicação de forma mais completa e holística, geralmente simulando comportamentos esperados (e os não esperados também!) do usuário. São testadas todas as camadas da aplicação, desde o fornecimento correto de dados até a exibição desses dados da melhor forma a garantir uma boa experiência ao usuário.
Esse tipo de teste simula o ambiente real da aplicação e comumente é executado por robôs programados (não usuários reais).
Por conta da sua abrangência ser a mais completa, esses testes são os mais custosos de se gerar e executar. Dessa forma, devem ser menores em quantidade.
Alguns exemplos de testes ponta a ponta:
- Usuário faz login no aplicativo com user “eu” e senha “123”
- Usuário vê uma tela com todos os seus pedidos
- Usuário clica no último pedido feito
- Tela com mais informações sobre o último pedido é exibida
Testes de Integração
Um bom ponto de partida para entendermos logo de cara a importância dos testes de integração é pesquisando em um site de buscas, imagens para a seguinte frase:
“Two unit tests, no integration tests”
Essa pesquisa nos retorna uma série de imagens com componentes separados que funcionam muito bem — isoladamente -, mas que em conjunto interagindo com outros componentes, há erros gritantes — uns até engraçados.
O ponto dessa pesquisa é justamente destacar um dos objetivos principais dos testes de integração → testar funcionalidades de forma completa, não o sistema como um todo e não um componente isolado da sua aplicação, evitando falhas como as que podem ser observadas nas imagens da pesquisa.
Os testes de integração são mais complicados e custosos de se gerar e executar do que os testes de unidade, porém bem mais simples do que os testes de ponta a ponta. Por conta disso, testes com esta abrangência olham para cenários que não podem ser cobertos pelos testes de ponta a ponta, assim como também olham para cenários que os testes de unidade já cobrem com êxito.
Testes de unidade
Uma curiosidade: A tradução correta de unit test é teste de unidade, não teste unitário, como comumente vemos por aí. A palavra “unitário” remete a “único”, que nesse contexto quer dizer “um único teste” (e não é isso que esperamos, não é mesmo?).
Os testes de unidade, por sua vez, irão focar em avaliar o funcionamento da menor parte testável da aplicação. Quando estamos falando de um sistema que utiliza a orientação a objetos, a menor parte testável pode se configurar como um método público em uma classe.
Uma grande vantagem da execução de testes de unidade é o seu retorno preciso → conseguimos saber exatamente onde está o problema que está gerando algum erro, tendo em vista que o escopo testado é o menor possível.
Outro ponto de destaque para os testes de unidade é que estes podem ser utilizados como instrumento para guiar a construção e design do código, por meio do que é conhecido como Test Driven Development, ou desenvolvimento orientado a testes (TDD). Este método nos força a pensar sobre o design do código primeiro e em como podemos quebrá-lo em peças menores.
Na nossa Pirâmide de Testes, os teste de unidade são a base, sendo os tipos de testes que levam menor tempo para serem criados e executados, e ao mesmo tempo, os que são maiores em quantidade.
Conclusão
A etapa de testagem de um software é crucial para garantir a qualidade do que desenvolvemos e principalmente, alcançar menores chances de erros e falhas que comprometam o desempenho de nosso sistema ou a experiência de nossos usuários.
Há muitas boas práticas e padrões que podem nos guiar na criação de testes eficientes e assertivos, e é importante estarmos sempre buscando conhecê-las e entender quais têm o maior potencial de melhorar a qualidade do que desenvolvemos — um resultado em que todos saem ganhando.
Ficou com alguma dúvida?
Sinta-se a vontade para entrar em contato ou deixar um comentário que iremos responder! 😊
Gostou do texto e quer saber mais sobre o conteúdo do gb.tech, a área de tecnologia do Grupo Boticário? Clique aqui e deixe seu e-mail! Buscamos cada vez mais dividir nosso conhecimento e estamos sempre procurando pessoas que queiram fazer beleza em nosso time. Vem pro gb.tech!