# Testes de Regressão: Garantindo Qualidade Contínua

**Meta Description:** Aprenda a implementar testes de regressão eficazes: estratégias de seleção, automação, manutenção e como construir uma suite de regressão sustentável.

---

## 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.

```python
# 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.

```yaml
# 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

```python
# 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

```python
# pytest.ini
[pytest]
addopts = -n auto --dist loadscope
# -n auto: usa todas CPUs disponíveis
# --dist loadscope: agrupa testes por scope
```

```yaml
# 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

```python
# 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

```python
# 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

```yaml
# 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% | 93% | ↑ |
| Execution Time | < 30 min | 35 min | ↓ |
| Flaky Rate | < 2% | 3% | → |
| Coverage | > 80% | 78% | ↑ |
| Maintenance Effort | < 20% | 25% | ↓ |

---

## Conclusão

Testes de regressão são a rede de segurança do seu software. Para uma suite eficaz:

1. **Selecione estrategicamente** - Nem tudo precisa de regressão completa
2. **Automatize** - Regressão manual não escala
3. **Mantenha** - Limpe testes obsoletos regularmente
4. **Monitore** - Métricas guiam melhoria
5. **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.
