Tipos de testes automatizados

Os nomes atribuídos aos diferentes tipos de testes tendem a ter temas comuns em todas as bases de código, mas não têm definições particularmente rigorosas. Este curso fornece algumas diretrizes sobre o que cada tipo de teste significa, mas outros recursos podem fornecer definições diferentes.

Nas páginas anteriores, houve exemplos de testes de unidade e de componente, que no nosso exemplo se referem a um componente do React. Podemos colocar os dois elementos em uma pirâmide de teste (ou outra forma), porque eles têm baixa complexidade e são rápidos de executar, mas podem não ter tanta utilidade quanto um teste de integração mais complexo.

Alguns exemplos de formas de estratégia de teste: uma pirâmide, um diamante cortado, uma casquinha de sorvete, um hexágono e um troféu.
A estratégia de teste tem todas as formas.

Tipos comuns de testes

Testes de unidades

Os testes de unidade têm o menor escopo. Elas tendem a ser usadas para testar pequenas partes de código, ou códigos puramente sem estado, quase de maneira matemática: se eu fornecer seu código com as entradas X, Y e Z, a saída dele será A, B e C.

O código com testes de unidade normalmente não tem dependências externas, como a busca em uma rede ou o uso implícito de outras funções ou bibliotecas. É um nó de árvore do código que você pode "recortar" e testar por conta própria.

Embora os testes de unidade tendem a ser rápidos de programar e executar, é sempre possível que testes de unidades pequenas de código não forneçam informações úteis. Muitas vezes, a falta de interação de uma unidade de código com outro código significa que é melhor testar em um nível mais alto para reduzir o risco.

Testes de componentes

Para desenvolvedores da Web, o nome "component" é sobrecarregado, geralmente significando um componente visível ao usuário, como um componente React ou um componente da Web. A definição mais geral é um fragmento do trabalho testável, por exemplo, uma classe com dependências externas. Para ser testado com eficácia, esse componente precisa ter as dependências simuladas ou ignoradas.

Como as práticas modernas de desenvolvimento da Web são baseadas no conceito de componente, os testes de componentes são uma maneira prática de analisar testes: por exemplo, você pode decidir que cada componente precisa de um teste. Os testes de componentes também são simples de acompanhar em contextos em que um único desenvolvedor ou pequena equipe reivindica claramente a propriedade de um componente. No entanto, pode ser difícil simular dependências complexas.

Testes de integração

Eles tendem a testar um pequeno grupo de componentes, módulos, subsistemas ou outras partes significativas do código em conjunto para garantir que eles funcionem corretamente. Essa é uma definição muito vaga. Para desenvolvedores da Web, imagine que o código que você está testando não é o build real de produção do seu site (ou até mesmo algo próximo), mas ainda conecta vários componentes relacionados do seu sistema.

Isso pode até incluir dependências "reais", como um banco de dados externo no modo de teste, em vez de uma simulação pura. Por exemplo, em vez de dizer que query() sempre vai retornar as mesmas duas entradas, o teste de integração pode confirmar que um banco de dados de teste tem algo. Os dados em si são menos importantes, mas agora você está testando se um banco de dados pode ser conectado e consultado com sucesso.

É possível programar testes de integração relativamente simples com implicações amplas que podem ser verificadas usando declarações, porque uma única ação conectada a vários componentes pode causar uma série de efeitos mensuráveis. Por isso, os testes de integração podem demonstrar efetivamente que seu sistema complexo será executado conforme o esperado. No entanto, eles podem ser difíceis de escrever e manter, e podem introduzir complexidade desnecessária. Por exemplo, gravar um FakeUserService para um teste de integração adiciona o requisito que tanto ele quanto a RealUserService precisam implementar um UserService.

Testes de fumaça

Esses são testes que precisam ser concluídos muito rapidamente e determinam se a base do código está em um estado adequado. Na prática, isso significa realizar testes simples no código que tem efeitos abrangentes na sua experiência.

Por exemplo, em um app da Web grande com login, isso pode garantir o funcionamento do sistema de login e autenticação, porque, sem ele, o app não pode ser utilizado e os testes adicionais são irrelevantes.

Os testes de fumaça podem ser bons candidatos para execução no script test do package.json em uma grande base de código. O teste manual também pode funcionar como uma espécie de teste preliminar.

Testes de regressão

O teste de regressão é um tipo de teste preliminar que garante que os recursos existentes continuem funcionando ou que bugs antigos não sejam reintroduzidos, após uma nova versão ou o desenvolvimento de outro recurso.

Isso está relacionado ao conceito de desenvolvimento orientado a testes (TDD, na sigla em inglês). Casos de teste gravados para acionar explicitamente um bug e usados depois para garantir que o bug seja corrigido, contam como casos de teste de regressão, porque a existência deles precisa impedir que esse mesmo bug seja retornado.

No entanto, o teste de regressão pode ser um problema sem uma ótima solução. É um termo muito citado pelas necessidades das empresas: à medida que os recursos são desenvolvidos, é importante que os antigos não corrompam. Uma base de código bem testada precisa ser capaz de manter isso, mas as bases de código reais nem sempre atendem a esse ideal. Isso será abordado mais em seções futuras.

Testes visuais

O teste visual envolve fazer capturas de tela ou gravar vídeos do estado de um site para comparar um bom estado conhecido (como uma captura de tela anterior) em relação à execução de teste atual. Por sua natureza, ele exige que um navegador real seja executado para que ele possa renderizar HTML, CSS e outras partes do site.

Em vez de testar visualmente testes completos que executam toda a base de código, pode ser útil criar "arbios" HTML que renderizem somente determinados componentes, especialmente em diferentes tamanhos de tela para acionar interfaces responsivas. Isso é mais complexo do que apenas usar o JSDOM ou estruturas semelhantes.

A falha nos testes visuais pode ser um bom sinal de outros tipos de falha. No entanto, interfaces complexas podem falhar em testes visuais por motivos não relacionados aos recursos que você está tentando testar, como outros novos recursos que mudam a aparência da interface ou até mesmo uma nova versão do SO que renderiza emojis de maneira diferente das versões anteriores.

Testes de ponta a ponta

Os testes de ponta a ponta geralmente ficam no topo da pirâmide de teste. Elas descrevem uma interação de experiência inteira com seu app da Web ou site, talvez centrada em um recurso específico e geralmente são executadas em um navegador controlado por um agente como WebdriverIO, Selenium ou Puppeteer, que pode executar sua base de código mais ou menos da mesma forma que ela seria implantada na produção (embora geralmente sejam veiculados no localhost).

Dependendo do site, isso pode envolver fazer login como usuário de teste, realizar ações importantes e confirmar se o site ou sistema está no estado correto. Vamos abordar mais exemplos desse tipo de teste em outras seções, porque eles podem ser muito eficientes, mas às vezes difíceis de manter.

Algumas táticas para simplificá-los podem incluir a redução do escopo ou a simulação de componentes específicos, quando relevante. Por exemplo, se os usuários precisarem fazer login no site, mas o login não for o recurso testado, defina uma sinalização para ambientes de teste que permita que o controlador de teste atue como um usuário sem fazer login ou criar os cookies associados.

Embora os testes completos possam ser maneiras muito eficientes de testar grandes seções transversais da sua base de código de uma só vez, esses testes em grande escala correm o risco de serem instáveis ou não confiáveis devido à dependência de sistemas externos. Muitas vezes, eles também podem deixar muitos dados de teste no banco de dados se, por exemplo, todos os testes criarem ou modificarem uma entrada. Acumular dados restantes dessa forma pode dificultar a determinação de como um teste falhou.

Teste de API

Os testes de API podem incluir a confirmação do comportamento das APIs fornecidas pelo software ou o acesso a APIs reais (possivelmente ativas) para confirmar o comportamento delas. De qualquer maneira, isso tende a testar as abstrações entre sistemas, ou seja, como eles se comunicarão entre si, sem realmente integrá-los como em um teste de integração.

Esses testes podem fornecer um precursor básico do teste de integração sem a sobrecarga de execução dos sistemas entre os quais você está testando as conexões. No entanto, os testes de sistemas reais podem ser instáveis.

Outros

Existem várias outras abordagens de teste que podem ser úteis, dependendo da fonte. Alguns exemplos interessantes:

  • Teste manual.
  • O teste de aceitação, um tipo de teste manual popularizado pelo Agile, confirma que o produto “atende às necessidades do usuário”.
  • O teste do caos refere-se à inserção de dados aleatórios para ver o que acontece, para garantir que um site não falhe se dados inválidos forem inseridos.
  • O teste de falhas simula intencionalmente falhas em sistemas complexos, como falhas de rede, para garantir que o código em teste responda de maneira controlada.
  • O teste de build confirma se os artefatos de build de uma base de código podem ser gerados, verificando se eles existem ou qual é o conteúdo deles. Esse tipo de teste pode ser útil para verificar a saída de um CMS complexo.

Cobertura de código

É possível medir qual porcentagem do seu código é testada por testes automatizados e relatar isso como uma estatística ao longo do tempo. Não recomendamos ter 100% de cobertura de código, porque isso pode levar a uma sobrecarga desnecessária, bem como testes simplificados ou mal projetados que não abrangem os principais casos de uso em profundidade.

A cobertura em si também pode ser uma ferramenta útil ao programar ou trabalhar em testes, especialmente nos de integração. Ao exibir um detalhamento percentual ou linha por linha de qual código é testado em um único teste, você pode ter insights sobre o que está faltando ou o que pode ser testado em seguida.

Recursos

Teste seu conhecimento

Quais das opções a seguir são tipos conhecidos de testes?

Testes visuais
Teste do caos
Teste de incêndio
Talvez você esteja criando software para um corpo de bombeiros.
Testes de diferenciação
Teste de estresse
Não mencionamos isso aqui, mas o teste de estresse ou de carga é um tipo de teste de sistemas de produção para garantir que eles possam aceitar uma grande quantidade de tráfego. Ela está mais associada ao design do sistema grande do que ao teste de bases de código mais comuns.