🧪 CAPÍTULO 17: ESTRATÉGIAS DE TESTE E AUTOMAÇÃO
🎯 1. Objetivos de Aprendizagem & Competências
Estimativa de Dedicação: 2 horas de estudo autoguiado.
Ao final deste capítulo, você será capaz de:
- 🔹 Dominar a Pirâmide de Testes de Mike Cohn: Testes Unitários (base rápida), Testes de Integração (meio com banco em memória) e Testes E2E (topo).
- 🔹 Diferenciar Testes de Caixa-Preta (baseados em requisitos/comportamento) de Testes de Caixa-Branca (baseados na cobertura estrutural de código e branches).
- 🔹 Aplicar técnicas de Mocking, Test Fixtures e injeção de dependências no
pytest. - 🔹 Implementar testes automatizados de endpoints HTTP com o
test_clientnativo do Flask e Pytest.
🏢 2. Cenário Corporativo & Estudo de Caso (TecProExpress)
Na TecProExpress, após uma atualização na rotina de cálculo de cubagem de fretes, o faturamento gerou R$ 45.000,00 em cobranças a menor antes que alguém percebesse o erro em produção. A equipe realizava apenas testes manuais clicando na interface do sistema.
O Desafio: Como Engenheiro de Testes de Software, você deve abolir testes manuais em rotinas críticas, construindo uma suíte automatizada com Pytest que execute 100% dos testes unitários e de integração em menos de 10 segundos a cada commit.
🧠 3. Fundamentação Teórica & Modelos Visuais
3.1. A Pirâmide de Testes de Mike Cohn
Estrutura recomendada para maximizar a velocidade de feedback e minimizar o custo de manutenção:
flowchart TD
subgraph PIRAMIDE ["PIRÂMIDE DE TESTES DE SOFTWARE"]
E2E["🌐 1. Testes Ponta a Ponta (E2E / UI)<br>Lentos, caros, poucos (Topo)"]
INT["⚙️ 2. Testes de Integração (APIs & DB em memória)<br>Velocidade média, validam contratos (Meio)"]
UNIT["🧪 3. Testes Unitários (Funções, Classes & Regras Puras)<br>Milissegundos, baratos, milhares (Base da Pirâmide)"]
E2E --> INT --> UNIT
end
style E2E fill:#fee2e2,stroke:#ef4444
style INT fill:#fef3c7,stroke:#d97706
style UNIT fill:#dcfce7,stroke:#16a34a
3.2. Caixa-Preta vs. Caixa-Branca
| Critério | Teste de Caixa-Preta (Funcional) | Teste de Caixa-Branca (Estrutural) |
|---|---|---|
| Conhecimento Interno | Não conhece o código-fonte (testa entradas e saídas). | Analisa o código-fonte, branches e loops. |
| Técnicas Comuns | Particionamento em Classes de Equivalência, Análise de Valor Limite. | Cobertura de Sentenças (Statement Coverage), Cobertura de Decisão/Branch. |
| Foco | O sistema atende aos Requisitos Funcionais acordados? | Todos os caminhos lógicos do algoritmo foram executados? |
💻 4. Aplicação Prática & Código Executável (Python 3.11+)
📋 Pré-requisitos e Instalação
Instale o Flask e o Pytest no seu ambiente virtual:
pip install flask pytest
O test_client() do Flask é nativo e não requer clientes HTTP adicionais (como httpx), tornando a suíte de testes extremamente rápida, isolada e sem dependências assíncronas.
💻 Código Completo e Autocontido (test_api_integracao.py)
Implementação de testes de integração com o test_client nativo do Flask e fixtures do Pytest:
"""
Módulo: test_api_integracao.py
Domínio: Testes de integração de endpoints REST com Flask test_client e Pytest.
"""
import pytest
from flask import Flask, request, jsonify
app = Flask(__name__)
BANCO_DADOS_MOCK = {}
@app.post("/itens/")
def criar_item():
dados = request.get_json()
if not dados:
return jsonify({"detail": "Payload JSON ausente"}), 400
nome = dados.get("nome")
preco = dados.get("preco", 0.0)
if not nome:
return jsonify({"detail": "Nome é obrigatório"}), 400
if preco <= 0:
return jsonify({"detail": "Preço deve ser positivo."}), 400
BANCO_DADOS_MOCK[nome] = preco
return jsonify({"status": "CRIADO", "item": nome, "preco": preco}), 201
@app.get("/itens/<nome>")
def consultar_item(nome: str):
if nome not in BANCO_DADOS_MOCK:
return jsonify({"detail": "Item não encontrado."}), 404
return jsonify({"item": nome, "preco": BANCO_DADOS_MOCK[nome]}), 200
# --- SUÍTE PYTEST ---
@pytest.fixture
def client():
"""Fixture Pytest que fornece o cliente de testes isolado do Flask."""
app.config["TESTING"] = True
BANCO_DADOS_MOCK.clear()
with app.test_client() as client:
yield client
def test_criar_item_com_sucesso(client):
resposta = client.post("/itens/", json={"nome": "Notebook", "preco": 3500.00})
assert resposta.status_code == 201
assert resposta.get_json()["item"] == "Notebook"
def test_criar_item_preco_invalido_deve_retornar_400(client):
resposta = client.post("/itens/", json={"nome": "Mouse", "preco": -10.00})
assert resposta.status_code == 400
assert "Preço deve ser positivo" in resposta.get_json()["detail"]
def test_consultar_item_inexistente_deve_retornar_404(client):
resposta = client.get("/itens/ItemFantasma")
assert resposta.status_code == 404
def test_fluxo_completo_cadastro_e_consulta(client):
post_res = client.post("/itens/", json={"nome": "Teclado", "preco": 250.00})
assert post_res.status_code == 201
get_res = client.get("/itens/Teclado")
assert get_res.status_code == 200
assert get_res.get_json()["preco"] == 250.00
if __name__ == "__main__":
print("🧪 Executando suíte com Pytest...")
pytest.main(["-v", __file__])
🚀 Como Executar
Você pode executar o arquivo diretamente com o interpretador Python ou através do runner do Pytest:
# Execução direta via Python
python test_api_integracao.py
# Ou via runner padrão do Pytest
pytest -v test_api_integracao.py
🖥️ Saída Esperada no Terminal
🧪 Executando suíte com Pytest...
============================= test session starts =============================
rootdir: C:\Temp\portal_mdbook
collected 4 items
test_api_integracao.py::test_criar_item_com_sucesso PASSED [ 25%]
test_api_integracao.py::test_criar_item_preco_invalido_deve_retornar_400 PASSED [ 50%]
test_api_integracao.py::test_consultar_item_inexistente_deve_retornar_404 PASSED [ 75%]
test_api_integracao.py::test_fluxo_completo_cadastro_e_consulta PASSED [100%]
============================== 4 passed in 0.05s ==============================
💡 5. Checkpoint de Engenharia & Boas Práticas
- Testes Independentes (FIRST): Os testes devem ser Fast (rápidos), Isolated (independentes da ordem de execução), Repeatable (repetíveis em qualquer ambiente), Self-validating (passa/falha sem inspeção manual) e Timely (escritos no momento certo).
- Anti-Pattern Cone de Sorvete (Ice Cream Cone): Ter milhares de testes manuais/E2E lentos e frágeis no topo e quase nenhum teste unitário na base. Inverta para o formato da Pirâmide!
🔗 6. Conexão com os Projetos Integradores
| Projeto Integrador | Como o conceito deste capítulo é aplicado no PI |
|---|---|
| PI-01 a PI-10 (Todos os 10 PIs) | Cada um dos 10 PIs possui uma suíte test_*.py completa com pytest e app.test_client() do Flask executando sobre SQLite em memória. |
🧪 7. Quiz de Fixação e Autoavaliação (Formative Assessment)
🧪 Quiz de Autoavaliação — Capítulo 17
🧪 Quiz de Autoavaliação — Capítulo 17
1. Na Pirâmide de Testes de Mike Cohn, por que os testes unitários devem compor a maior base da suíte de testes?
- A) Porque eles são os únicos testes aceitos pela nuvem da AWS.
- B) Porque são extremamente rápidos (executam em milissegundos), baratos de manter e apontam com precisão exata a linha e função onde o bug ocorreu.
- C) Porque dispensam a escrita de código-fonte.
- D) Porque substituem a necessidade de requisitos de negócio.
💡 Ver Resposta e Justificativa
Resposta Correta: B
Justificativa: Testes unitários isolam unidades mínimas de código, fornecendo feedback instantâneo ao desenvolvedor durante a digitação e custando uma fração do tempo de testes E2E.
2. A técnica de teste de Caixa-Preta conhecida como 'Análise de Valor Limite' (Boundary Value Analysis) orienta a testar:
- A) Apenas números pares e inteiros positivos.
- B) Os valores nas fronteiras exatas das classes de equivalência (ex: se a regra aceita idades de 18 a 65 anos, testar 17, 18, 19, 64, 65 e 66).
- C) O consumo máximo de memória do servidor.
- D) A cor dos botões na interface gráfica.
💡 Ver Resposta e Justificativa
Resposta Correta: B
Justificativa: A maioria dos erros lógicos de programação ocorre nas bordas das condições relacionais (< vs <=), tornando o teste dos valores limites altamente eficaz.
3. O que é um 'Mock' no contexto de testes automatizados?
- A) Um bug insolúvel que deve ser ignorado.
- B) Um objeto simulado que reproduz o comportamento de uma dependência externa (como um banco de dados real ou API externa de pagamentos) de forma controlada e sem chamadas de rede reais.
- C) Uma falha de sintaxe do Python.
- D) Um tipo de licença de software livre.
💡 Ver Resposta e Justificativa
Resposta Correta: B
Justificativa: Mocks isolam a unidade sob teste, permitindo simular respostas de sucesso, lentidão ou erros de serviços externos sem custos e sem instabilidades de rede.
🛠️ 8. Ponte para a Ação: Laboratório Prático
Coloque esta teoria em prática executando o roteiro de laboratório autoguiado:
👉 ATIVIDADE 09: QUALIDADE E TESTES COM PYTEST
📌 9. Resumo Executivo & Key Takeaways
- Pirâmide de Testes: Base ampla de Testes Unitários + Camada intermediária de Integração + Topo enxuto de E2E.
- Caixa-Preta vs Caixa-Branca: Foco em comportamento funcional e valores limites versus cobertura estrutural de branches.
- FIRST Principles: Testes devem ser rápidos, isolados, repetíveis, auto-validáveis e tempestivos.
- Automação no CI: Nenhum código deve ser aceito em produção sem que 100% da suíte de testes automatizados esteja verde.