Testes Automatizados: Do Básico ao Avançado Já leu

Fundamentos de Testes Automatizados Os testes automatizados são a espinha dorsal do desenvolvimento moderno. Enquanto testes manuais consomem tempo e são propensos a erros humanos, os testes automatizados executam verificações repetitivas com precisão, permitindo que você implante código com confiança. A pirâmide de testes apresenta três níveis: testes unitários (base), testes de integração (meio) e testes end-to-end (topo). Comece pelos unitários — são rápidos, isolados e testam uma única unidade de código. Vamos implementar um teste unitário básico em Python usando . Considere uma função que calcula o preço final com desconto: Execute com . Cada teste é isolado, testando um comportamento específico. Isso garante que refatorações futuras não quebrem funcionalidades esperadas. Testes de Integração e Mocks Testes de integração verificam como múltiplos componentes trabalham juntos. Aqui entra em jogo o uso de mocks — objetos simulados que substituem dependências externas como APIs, bancos de dados ou serviços. Isso permite testar lógica sem depender de sistemas externos. Considere uma aplicação

Fundamentos de Testes Automatizados

Os testes automatizados são a espinha dorsal do desenvolvimento moderno. Enquanto testes manuais consomem tempo e são propensos a erros humanos, os testes automatizados executam verificações repetitivas com precisão, permitindo que você implante código com confiança. A pirâmide de testes apresenta três níveis: testes unitários (base), testes de integração (meio) e testes end-to-end (topo). Comece pelos unitários — são rápidos, isolados e testam uma única unidade de código.

Vamos implementar um teste unitário básico em Python usando pytest. Considere uma função que calcula o preço final com desconto:

def aplicar_desconto(preco, percentual_desconto):
    if percentual_desconto < 0 or percentual_desconto > 100:
        raise ValueError("Desconto deve estar entre 0 e 100")
    return preco * (1 - percentual_desconto / 100)

# Teste unitário
def test_aplicar_desconto_valido():
    assert aplicar_desconto(100, 10) == 90.0
    assert aplicar_desconto(50, 50) == 25.0

def test_aplicar_desconto_invalido():
    with pytest.raises(ValueError):
        aplicar_desconto(100, 150)

Execute com pytest nome_arquivo.py. Cada teste é isolado, testando um comportamento específico. Isso garante que refatorações futuras não quebrem funcionalidades esperadas.

Testes de Integração e Mocks

Testes de integração verificam como múltiplos componentes trabalham juntos. Aqui entra em jogo o uso de mocks — objetos simulados que substituem dependências externas como APIs, bancos de dados ou serviços. Isso permite testar lógica sem depender de sistemas externos.

Considere uma aplicação que busca dados de um usuário em uma API:

from unittest.mock import patch, MagicMock
import requests

def buscar_usuario(user_id):
    response = requests.get(f"https://api.example.com/users/{user_id}")
    if response.status_code == 200:
        return response.json()
    raise Exception("Usuário não encontrado")

@patch('requests.get')
def test_buscar_usuario_sucesso(mock_get):
    mock_get.return_value = MagicMock(status_code=200)
    mock_get.return_value.json.return_value = {"id": 1, "nome": "João"}

    resultado = buscar_usuario(1)
    assert resultado["nome"] == "João"
    mock_get.assert_called_once_with("https://api.example.com/users/1")

@patch('requests.get')
def test_buscar_usuario_erro(mock_get):
    mock_get.return_value = MagicMock(status_code=404)

    with pytest.raises(Exception):
        buscar_usuario(1)

O decorator @patch substitui a função real por um mock. Você controla o comportamento e verifica se as chamadas ocorreram corretamente. Isso torna testes rápidos e confiáveis, sem dependências externas.

Testes End-to-End e Automação de Interface

Testes end-to-end (E2E) simulam a jornada real do usuário, testando a aplicação completa do navegador até o banco de dados. Selenium e Playwright são frameworks populares para essa automação. Eles controlam navegadores reais, clicam em elementos e verificam resultados.

Aqui está um exemplo com Playwright em Python:

from playwright.sync_api import sync_playwright

def test_login_usuario():
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page()

        # Navegar até a aplicação
        page.goto("https://exemplo.com/login")

        # Preencher formulário
        page.fill("input[name='email']", "usuario@example.com")
        page.fill("input[name='senha']", "senha123")
        page.click("button:has-text('Entrar')")

        # Aguardar redirecionamento e verificar
        page.wait_for_url("https://exemplo.com/dashboard")
        assert page.is_visible("h1:has-text('Dashboard')")

        browser.close()

Testes E2E são lentos e frágeis se não bem estruturados — use-os para fluxos críticos. Dica profissional: mantenha seletores CSS descritivos e evite esperas fixas (use wait_for_* ao invés de time.sleep()).

Boas Práticas e Estratégias Avançadas

Cobertura de Testes e Métricas

A cobertura de testes indica qual percentual do código é executado durante testes. Use coverage em Python para medir:

pip install coverage
coverage run -m pytest
coverage report
coverage html  # Gera relatório HTML detalhado

Busque 80-90% de cobertura em código crítico, não 100% — é overkill e caro. Foque em lógica de negócio complexa.

Estrutura AAA e Nomenclatura Clara

Organize seus testes com a estrutura Arrange-Act-Assert:

def test_carrinho_adiciona_produto():
    # Arrange: preparar dados
    carrinho = Carrinho()
    produto = Produto("Notebook", 3000)

    # Act: executar ação
    carrinho.adicionar(produto)

    # Assert: verificar resultado
    assert len(carrinho.itens) == 1
    assert carrinho.total == 3000

Nomeie testes descritivamente: test_[o_que_esta_sendo_testado]_[condicao]_[resultado_esperado].

Testes Parametrizados

Evite repetição usando parametrização:

import pytest

@pytest.mark.parametrize("entrada,esperado", [
    (2, 4),
    (3, 9),
    (-1, 1),
])
def test_quadrado(entrada, esperado):
    assert entrada ** 2 == esperado

Um teste, múltiplos cenários. Isso torna suites de testes mais concisas e eficientes.

Conclusão

Testes automatizados são investimento essencial em qualidade e velocidade. Comece com testes unitários para lógica isolada, use mocks para integração sem dependências externas, e implemente E2E para fluxos críticos. Mantenha testes simples, legíveis e rápidos — uma suite lenta é um bloqueio ao desenvolvimento. Com disciplina, seus testes se tornarão documentação viva do comportamento esperado do sistema, permitindo refatorar com segurança e entregar valor continuamente.

Referências


Artigos relacionados