Testes automatizados: como melhorar qualidade e confiabilidade do seu software

Testes automatizados: como melhorar qualidade e confiabilidade do seu software
Neilton Seguins
Neilton Seguins

Compartilhe

E aí, tudo bem? Espero que sim!

Estava ouvindo uma playlist muito boa enquanto escrevia este artigo e fiquei pensando: “Já pensou se uma banda não fizer nenhum ensaio antes do show?".

Será que ela ia fazer um bom show? Talvez enquanto o guitarrista estivesse no Lá o baixista estaria no Sol (sol lamento) e nada ia funcionar.

Imagina só: seria um desastre, não é mesmo?

Bom, a gente pode usar essa mesma ideia para o desenvolvimento de softwares. Afinal de contas, sua aplicação sem testes funciona mais ou menos da mesma forma que um show sem ensaio.

Os testes automatizados são, portanto, como os ensaios antes do show: é possível testar cada nota, cada luz e cada interação para garantir que o espetáculo seja incrível.

E o melhor: se adicionar algum efeito especial de última hora, vai só melhorar o show, pois os testes garantem que nada vai desandar.

Já deu para perceber a importância disso, não é mesmo? Pensando nisso, neste artigo vamos conhecer os testes automatizados e como eles nos ajudam a desenvolver software melhor.

Você vai aprender:

  • O que são testes automatizados e por que são importantes.
  • Os diferentes tipos de testes e quando usar cada um:
  • Testes de unidade
  • Testes de integração
  • Testes de ponta a ponta
  • Alguns testes manuais e como eles complementam os automatizados.
  • Ferramentas e frameworks mais usadas para cada tipo de teste.
  • Melhores práticas para escrever testes

E muito mais. Vamos nessa?

Por que é essencial fazer testes automatizados?

Sem testes, mexer no código é como puxar aquele fio solto da sua roupa: você nunca sabe o que irá desmanchar.

Por exemplo, ao ajustar a lógica para cálculo do frete em uma loja online, pode ser que, sem perceber, você quebre o processo de finalizar uma compra.

Aí que os testes automatizados fazem a diferença, eles garantem que tudo continua funcionando como esperado, mesmo após mudanças.

Testar dá confiança. Você pode evoluir seu código sem medo de causar desastres inesperados. É como pesquisar no Google (ou GPT) aquela palavra que você não lembra como escreve antes de digitá-la em um email importante (risos).

Além disso, testes automatizados facilitam e agilizam o que chamamos de integração contínua (CI) e entrega contínua (CD).

Se quiser se aprofundar nestes temas, na formação da Alura você terá o melhor conteúdo sobre o assunto.

Banner promocional da Alura, com um design futurista em tons de azul, apresentando o texto

Tipos de testes automatizados

Agora é hora de entender os diferentes tipos de testes e como usá-los para testar suas aplicações.

Teste de unidade

Testes de unidade verificam o menor pedaço de funcionalidade do sistema: funções, métodos ou componentes isolados.

Eles são rápidos, fáceis de escrever e devem cobrir o maior número possível de cenários.

Quando usar:

  • Sempre que houver uma lógica isolada crucial para aplicação e que possa quebrar.
  • Para verificar cálculos, validações e pequenos componentes.

Ferramentas populares

As ferramentas populares para fazer testes de unidade são:

Exemplo de teste de unidade com Jest

Vamos ver um exemplo com Jest.

// Função para calcular o desconto
function calcularValorComDesconto(preco, desconto) {
  if (desconto < 0 || desconto > 100) throw new Error('Desconto inválido');
  return preco - (preco * desconto) / 100;
}

// Teste unitário
test('calcular desconto corretamente', () => {
  const resultado = calcularValorComDesconto(100, 20);
  expect(resultado).toBe(80); // valor esperado
});

Normalmente o teste possui uma descrição e uma função que faz a verificação se aquele trecho de código faz exatamente o esperado. Neste caso, seria o valor com o desconto já aplicado.

Os testes de unidade são simples e normalmente testam uma única coisa. Devem cobrir o maior número de cenários, quanto mais na sua aplicação, melhor.

E como seria testar componentes e partes da sua aplicação que interagem entre si? É aí que entram os testes de integração.

Testes de integração

Testes de integração verificam como diferentes partes do sistema funcionam juntas. Por exemplo, como um componente do front-end se comunica com uma API no back-end.

Quando usar:

  • Sempre que diferentes módulos precisarem interagir.
  • Para validar fluxos de trabalho completos, como cadastro de usuários.

Ferramentas populares de teste de integração

As mesmas ferramentas que servem para criar os testes de unidade servem perfeitamente para criar testes de integração.

Mas em alguns casos, como em aplicações front-end desenvolvidas com React ou Angular, existem frameworks que tornam sua vida bem mais fácil, como:

Esses frameworks melhoram a experiência de testar aplicações com React e Angular.

Por exemplo, considere o componente Counter abaixo:

import React, { useState } from "react";

export const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Current Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

Quando o botão “Increment” for clicado, o estado do contador muda. Se queremos testar a integração destes dois componentes, podemos fazer o seguinte teste:

import { render, screen, userEvent } from "@testing-library/react";
import { Counter } from "./Counter";

test("deve incrementar o contador ao clicar no botão", () => {
  render(<Counter />);

  // Verifica o texto inicial
  expect(screen.getByText("Current Count: 0")).toBeInTheDocument();

  // Clica no botão
  userEvent.click(screen.getByText("Increment"));

  // Verifica se o contador foi incrementado
  expect(screen.getByText("Current Count: 1")).toBeInTheDocument();
});

Repare só que o render tem o trabalho de renderizar o componente dentro do teste. O screen acessa como se fosse uma cópia do DOM naquele momento, e com ele a gente pode pegar o elemento com o texto especificado e verificar se ele está no documento.

Já o userEvent, simula uma ação do usuário, clicando no botão. Ao clicar no botão o contador soma 1, então o texto já deve ser “Current Count: 1” e não mais “Current Count: 0” como era antes.

Não estamos mais testando componentes isolados, ou funcionalidades isoladas. Agora estamos testando o que é esperado quando clicamos no botão, e como isso afeta outras partes da aplicação.

Ouça neste podcast sobre testes de integração, o dia a dia e as dores que fazem parte da rotina das pessoas que lidam com Flutter para o desenvolvimento mobile.

Mesmo testando esse tipo de integração na aplicação este tipo de teste é mais demorado de implementar se precisamos testar uma aplicação inteira ou grandes fluxos. É aí que entram os testes de ponta a ponta, ou testes E2E (end to end).

Testes de ponta a ponta

Os testes E2E simulam a experiência real do usuário. Eles garantem que o sistema inteiro funcione de ponta a ponta, como esperado.

Quando usar:

  • Para validar fluxos críticos (e.g., fazer login, completar uma compra, fazer uma reserva).

Ferramentas populares de testes de ponta a ponta

As ferramentas populares de testes de ponta a ponta são:

Vamos ver um exemplo com o Cypress:

describe('Fluxo de Login', () => {
  it('deve permitir login com credenciais válidas', () => {
    cy.visit('/login');
    cy.get('#usuario').type('neilton');
    cy.get('#senha').type('senha123');
    cy.get('#entrar').click();
    cy.contains('Bem-vindo, Neilton');
  });
});

Neste exemplo, testamos o fluxo de login de uma aplicação. Repare que agora temos uma estrutura diferente. Temos um describe que pode descrever o teste ou conjunto de testes.

Já o teste em si é descrito pelo it que em tradução livre deve significar (lendo a descrição do teste junto) “Isso deve permitir o login com credenciais válidas”.

O Cypress, assim como as outras ferramentas de teste, abrem um navegador de sua escolha, e simulam a interação da pessoa usuária com a aplicação, simulando cliques, digitação, navegação e muitas outras coisas.

São perfeitos para cenários críticos, como fluxos como cadastro, login, compras, reservas, e muito mais.

Ouça neste podcast sobre a necessidade de entender os mais variados testes de software, os primeiros passos com testes, e como é o fluxo de testes no dia a dia do mercado.

Qual teste devo fazer na minha aplicação?

Agora que você já conhece os testes, você pode estar se perguntando qual aplicar em seus projetos?

Fazer apenas testes de unidade é o suficiente? Preciso ter sempre testes de ponta a ponta ou só alguns testes de integração bastam?

Bom, a resposta para isso é a clássica resposta de sênior: depende. Depende do seu projeto, da complexidade dele, tamanho, se ele possui muitas integrações com API’s, banco de dados, fluxos críticos que devem ser testados, entre outras coisas.

Ouça neste podcast como a Avenue, empresa do ramo financeiro e de investimentos, realiza cobertura de testes e tem estabelecido uma cultura centrada em testes e qualidade.

Para te ajudar a entender melhor como os testes funcionam no mercado, vamos analisá-los comparando o tempo para implementar cada um e o custo que isso normalmente leva.

Um diagrama em forma de pirâmide que ilustra a relação entre o tempo e o custo de diferentes tipos de testes de software. Na base da pirâmide, temos os testes de Unidade, que são rápidos e de baixo custo. Em seguida, os testes de Integração, que possuem um custo e tempo intermediários. No topo, estão os testes End to End (E2E), que são mais lentos e caros. O eixo vertical à esquerda indica o tempo e o eixo à direita representa o custo. Há ícones de uma tartaruga e de um coelho nos extremos do eixo de tempo.

Comparação entre os tipos de testes automatizados

Primeiramente, vamos considerar a base da pirâmide. Testes unitários testam uma unidade, como uma única função ou um utilitário.

Os testes de unidade são mais simples, então são de fácil e rápida implementação e o custo é menor. É possível testar toda a sua aplicação apenas com testes unitários.

No meio da pirâmide, temos os testes de integração. Trata-se de um processo mais complexo, que confere como diferentes partes da aplicação trabalham em conjunto e interagem entre si.

Eles são mais robustos, então a implementação não é tão rápida e o custo é mais elevado e você deve considerar se vale a pena implementá-los.

No topo, temos os testes de ponta a ponta, utilizados para testar toda a aplicação. Checa-se como a pessoa usuária interage com a aplicação desde que ela abre o navegador e acessa a página, verificando todo o fluxo.

São difíceis de implementar e, consequentemente, requerem um investimento maior também. Então se sua aplicação é uma to do list e não um grande e-commerce, não considere fazer testes de ponta a ponta.

Nada aqui é uma regra, ok? Sempre avalie que tipo de testes fazer, pois isso pode te economizar um bom tempo e dinheiro.

Testes visuais e exploratórios

Ok, já conhecemos o que são testes automatizados, os principais tipos, ferramentas e frameworks que facilitam escrever alguns testes, mas você pode estar se perguntando se existem só esses tipos de testes.

E a resposta é: não. Existem testes exploratórios, que são testes feitos de forma manual e não seguem um script definido.

Essa técnica permite que a pessoa que irá testar a aplicação siga sua experiência e criatividade para encontrar bugs e problemas que podem ter sido negligenciados em outros tipos de testes.

Os testes exploratórios são valiosos por permitirem a descoberta de defeitos inesperados. Geralmente realizados com pouca frequência em grandes projetos.

Outro tipo de teste muito comum são os testes de regressão visual. Estes testes garantem que mudanças no código não alterem o design ou layout da aplicação de maneira inesperada.

Com estes testes você consegue garantir que sua aplicação segue o padrão de qualidade visual, acessibilidade e estilos.

Um botão com texto desalinhado? Com testes visuais você não tem isso. Ferramentas como Percy, Loki e Chromatic são muito usadas para criação de testes visuais.

Melhores práticas para testes automatizados

Falamos muito sobre os tipos de testes automatizados e até vimos exemplos com o Jest. Os testes precisam seguir algumas práticas para serem consistentes e garantir a confiabilidade da aplicação.

Para isso, vou deixar algumas das melhores práticas adotadas no mercado para quem escreve testes.

Padrão AAA

O padrão AAA organiza os testes em três etapas claras:

  1. Arrange (Preparar): Configure o ambiente e os dados necessários.
  2. Act (Executar): Realize a ação que será testada.
  3. Assert (Verificar): Valide se o resultado é o esperado.

Veja o exemplo abaixo:

test('deve somar dois números', () => {
  // Arrange
  const num1 = 2, num2 = 3;

  // Act
  const resultado = somar(num1, num2);

  // Assert
  expect(resultado).toBe(5);
});

Repare como este teste possui uma boa descrição e uma estrutura que facilita a leitura do que ele testa.

Primeiro o Arrange prepara os dados que serão usados no teste. O Act é a execução do que estamos testando e por fim o Assert é a verificação do resultado esperado.

Adotar o padrão AAA ajuda você e outras pessoas a entenderem o que está sendo testado e facilita a manutenção do código no futuro.

Outra dica importante é que você pode organizar conjuntos de testes usando o describe, suportado por algumas ferramentas como o jest, vitest e cypress. Por exemplo:


describe("Função somar”, () => {
  test("deve retornar 3 quando somamos 1 e 2", () => {
    expect(sum(1, 2)).toBe(3);
  });

  test("deve retornar -1 quando somamos -2 e 1", () => {
    expect(sum(-2, 1)).toBe(-1);
  });

  test("deve retornar 0 quando somamos 0 e 0", () => {
    expect(sum(0, 0)).toBe(0);
  });
});

Com isso você pode agrupar testes que testam uma mesma unidade em diferentes cenários.

Test-Driven Development (TDD)

TDD ou Desenvolvimento Guiado por Testes é uma abordagem onde você escreve os testes antes de escrever o código funcional.

Parece estranho no início, mas é uma forma muito interessante de garantir que o código atende exatamente ao que foi solicitado.

A ideia central é iterar em pequenos ciclos:

  1. Escreva um teste que falha (porque o código funcional ainda não existe).
  2. Implemente o código funcional mínimo necessário para passar no teste.
  3. Refatore o código, mantendo os testes verdes, isto é, eles devem passar agora.

Esse ciclo é conhecido como Red, Green, Refactor.

Vantagens do TDD:

  • Ajuda a prever bugs antes que eles aconteçam.
  • Foca no que o código precisa fazer, não em como fazer.
  • Facilita a manutenção a longo prazo.

Conclusão

Testar pode parecer trabalhoso no início, mas o seu eu do futuro com certeza irá agradecer seu eu do passado por ele ter escrito testes.

Com os tipos de testes certos, ferramentas adequadas e boas práticas, você terá um sistema mais confiável e tempo para focar no que realmente importa: criar novos bugs que você resolverá escrevendo testes (risos).

Para te ajudar a se tornar um especialista em criar testes automatizados de software vou deixar abaixo algumas formações que farão a diferença na sua carreira, independente se você é QA (Quality Assurance, nome dado às pessoas que testam a aplicação e garantem sua qualidade, vulgo inimigas do dev rs), desenvolvedor(a) front end e backend. Apenas faça e me conte depois, Ok?

Ah, e se quiser ouvir a mesma playlist que eu estava ouvindo enquanto escrevia este artigo, clica aqui neste link e aproveite!

Até a próxima!

Referências

Neilton Seguins
Neilton Seguins

Sou graduado como Bacharel em Ciência e Tecnologia e em Engenharia Mecânica. Atuo como Instrutor de Desenvolvedor de Software na Alura e possuo experiência com desenvolvimento usando JavaScript/TypeScript, React js, Next js e Node.js. Amo compartilhar conhecimento, pois acredito que a educação é transformadora e quero mudar a vida de pessoas através da educação assim como consegui mudar de vida. Também amo ouvir e tocar música, ler livros e mangás e assistir séries.

Veja outros artigos sobre Front-end