O Que São Testes de Regressão?
Testes de regressão são executados para garantir que mudanças recentes no código (novas funcionalidades, bug fixes, refactoring) não introduziram novos defeitos ou quebraram funcionalidades existentes.
Quando Executar Regressão
| Situação | Recomendação |
|---|---|
| Antes de cada release | Suite completa |
| Após hotfix | Smoke + área afetada |
| Antes de deploy | Critical path |
| Semanalmente (CI) | Suite rápida |
| Sob demanda | Após mudanças críticas |
Estratégias de Seleção de Testes
1. Regressão Completa
Executa toda a suite de testes.
Quando usar:
- Releases major
- Mudanças arquiteturais
- After database migration
- Final gate para produção
2. Regressão Parcial (Seletiva)
Seleciona subconjunto baseado em mudanças.
# Exemplo: Selecionar testes por área afetada
def get_affected_tests(changed_files):
"""Retorna testes que devem ser executados"""
module_to_tests = {
'src/auth/': ['tests/auth/*.py', 'tests/conftest.py'],
'src/payment/': ['tests/payment/*.py'],
'src/cart/': ['tests/cart/.py', 'tests/checkout/.py'],
}
tests = set()
for changed_file in changed_files:
module = extract_module(changed_file)
if module in module_to_tests:
tests.update(module_to_tests[module])
return tests
# Executar apenas testes afetados
affected_tests = get_affected_tests(get_changed_files())
pytest.main(affected_tests)
3. Regressão Baseada em Risco
Prioriza por risco de impacto.
| Prioridade | Critério | Execução |
|---|---|---|
| Crítica | Fluxos de pagamento, auth | Sempre |
| Alta | Features principais | Antes de cada release |
| Média | Features secundárias | Semanalmente |
| Baixa | Edge cases | Mensalmente |
4. Regressão Híbrida
Combina múltiplas estratégias.
# strategy: Hybrid Regression
# Nivel 1: Critical (sempre)
- tests/auth/login*.py
- tests/auth/logout*.py
- tests/payment/checkout*.py
- tests/smoke/*.py
# Nivel 2: Based on changes- get_affected_tests()
# Nivel 3: Risk-based- tests/high_risk_features*.py
# Nivel 4: Full (releases only)- tests/full_suite/*.py
Automação de Regressão
Framework Ideal
regression_suite/
├── conftest.py # Setup global
├── pytest.ini # Configuração
├── requirements.txt # Dependências
│
├── core/
│ ├── __init__.py
│ ├── base_page.py # Page Object base
│ ├── api_client.py # Cliente API
│ └── database.py # Helpers de DB
│
├── pages/ # Page Objects
│ ├── login_page.py
│ ├── dashboard_page.py
│ └── checkout_page.py
│
├── api/ # API Tests
│ ├── test_auth.py
│ ├── test_products.py
│ └── test_orders.py
│
├── ui/ # UI Tests
│ ├── test_login.py
│ ├── test_checkout.py
│ └── test_profile.py
│
├── smoke/ # Smoke Tests
│ └── test_critical_path.py
│
├── data/ # Data files
│ └── users.json
│
└── reports/ # Relatórios
└── .gitkeep
Page Object Pattern
# pages/base_page.py
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class BasePage:
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def click(self, locator):
self.wait.until(EC.element_to_be_clickable(locator)).click()
def send_keys(self, locator, text):
element = self.wait.until(EC.presence_of_element_located(locator))
element.clear()
element.send_keys(text)
def get_text(self, locator):
return self.wait.until(EC.presence_of_element_located(locator)).text
def is_visible(self, locator):
try:
return self.wait.until(
EC.visibility_of_element_located(locator)
).is_displayed()
except:
return False
# pages/login_page.py
from selenium.webdriver.common.by import By
class LoginPage(BasePage):
URL = "https://exemplo.com/login"
EMAIL = (By.ID, "email")
PASSWORD = (By.ID, "password")
LOGIN_BUTTON = (By.ID, "login-button")
ERROR_MESSAGE = (By.CLASS_NAME, "error-message")
def load(self):
self.driver.get(self.URL)
return self
def login(self, email, password):
self.send_keys(self.EMAIL, email)
self.send_keys(self.PASSWORD, password)
self.click(self.LOGIN_BUTTON)
return self
def get_error(self):
return self.get_text(self.ERROR_MESSAGE)
Execução Paralela
# pytest.ini
[pytest]
addopts = -n auto --dist loadscope
# -n auto: usa todas CPUs disponíveis
# --dist loadscope: agrupa testes por scope
# GitHub Actions
name: Regression Tests
on:
schedule:
- cron: '0 6 1' # Toda segunda 6h
jobs:
regression:
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- name: Run test shard ${{ matrix.shard }}
run: |
pytest tests/
--dist loadscope
--maxprocesses 4
--split=${{ matrix.shard }}/4
Manutenção da Suite
Identificando Testes Obsoletos
# script: identify_obsolete_tests.py
import subprocess
from datetime import datetime
def analyze_test_usage():
"""Analisa quais testes não foram executados ou falham sempre"""
results = []
# 1. Testes sem execution recent
git_log = subprocess.run(
['git', 'log', '--oneline', '--all', '--', 'tests/'],
capture_output=True,
text=True
).stdout
# 2. Testes que sempre falham
# 3. Testes duplicados
# 4. Cobertura redundante
return results
# Gerar relatório
report = analyze_test_usage()
print("Testes para revisão:")
for test in report:
print(f" - {test['name']}: {test['reason']}")
Indicadores de “Code Smell” em Testes
# PROBLEMA: Teste faz muito
def test_criar_pedido_completo():
# Setup (50 linhas)
# Login (10 linhas)
# Add produto (15 linhas)
# Add outro produto (15 linhas)
# Apply cupom (10 linhas)
# Calculate shipping (10 linhas)
# Checkout (20 linhas)
# Payment (30 linhas)
# Verify (40 linhas)
# Cleanup (20 linhas)
# Total: 220 linhas - MUITO PARA UM TESTE!
# SOLUÇÃO: Dividir em múltiplos testes focados
def test_adicionar_produto_ao_carrinho():
"""Focado: adiciona produto"""
pass
def test_aplicar_cupom_desconto():
"""Focado: aplica cupom"""
pass
def test_calcular_frete():
"""Focado: calcula frete"""
pass
def test_checkout_com_sucesso():
"""Integração: fluxo completo"""
pass
Métricas de Regressão
Dashboard
# Grafana Dashboard - Regression Suite
panels:
- title: "Test Execution Time"
type: graph
queries:
- query: "sum(rate(test_duration_seconds_sum[5m])) / sum(rate(test_duration_seconds_count[5m]))"
- title: "Pass/Fail Rate"
type: piechart
queries:
- query: "sum by (status) (test_results_total)"
- title: "Flaky Tests"
type: stat
queries:
- query: "count(test_flaky == 1)"
- title: "Coverage Trend"
type: timeseries
queries:
- query: "test_coverage_percentage"
KPIs
| KPI | Target | Current | Trend |
|---|---|---|---|
| Pass Rate | > 95{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} | 93{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} | ↑ |
| Execution Time | < 30 min | 35 min | ↓ |
| Flaky Rate | < 2{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} | 3{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} | → |
| Coverage | > 80{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} | 78{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} | ↑ |
| Maintenance Effort | < 20{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} | 25{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} | ↓ |
Conclusão
Testes de regressão são a rede de segurança do seu software. Para uma suite eficaz:
- Selecione estrategicamente – Nem tudo precisa de regressão completa
- Automatize – Regressão manual não escala
- Mantenha – Limpe testes obsoletos regularmente
- Monitore – Métricas guiam melhoria
- Priorize – Risco e impacto guiam execução
FAQ
P: Quantos testes deve ter na suite de regressão?
R: Depende do projeto. Uma suite saudável cobre funcionalidades críticas com alta cobertura.
P: Como reduzir flaky tests?
R: Use waits explícitos, isole testes, evite dependências, investigue root cause.
P: Testes de regressão devem ser no CI ou separate job?
R: Smokes no CI (bloqueante), regressão completa em job separado (não bloqueante).
P: Como saber se a suite precisa de mais testes?
R: Analise: bugs que passaram, áreas sem cobertura, novos riscos.
