# Swe Testing

> Diretrizes de testes para projetos Python. Use quando o usuário pedir para escrever testes, aplicar TDD, revisar cobertura, criar testes de contrato, configurar pytest, testar APIs FastAPI/Django, mockar dependências, configurar CI com testes, ou qualquer tarefa relacionada a qualidade via testes automatizados.

- Skill: `nxs-cafi/swe-testing` (Agent Skill)
- Install (CLI): `npx skillmds@latest add nxs-cafi/swe-testing`
- Raw SKILL.md: https://api.skillmd.com/api/skills/nxs-cafi/swe-testing/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: nxs-cafi (https://skillmd.com/u/nxs-cafi)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/nxs-cafi/swe-testing

---


# Testes — Python SWE Agent

## Pirâmide de Testes — Proporções Esperadas

```
         /\
        /  \      E2E / Integration (10-15%)
       /----\
      /      \    Integration / API (20-30%)
     /--------\
    /          \  Unit Tests (55-70%)
   /____________\
```

**Regra prática:** Para cada Use Case, espera-se:
- N testes unitários de domínio (entidades, value objects, domain services)
- 1-2 testes de integração do use case
- 1 teste de API (happy path + principal error path)

## TDD — Workflow Obrigatório para Lógica de Negócio

```
RED → GREEN → REFACTOR

1. Escreva o teste que falha (descreve o comportamento esperado)
2. Escreva o mínimo de código para passar
3. Refatore sem quebrar o teste
```

### Exemplo TDD Completo
```python
# PASSO 1: RED — teste descreve o comportamento
def test_money_addition_same_currency():
    price = Money(Decimal("10.00"), "BRL")
    tax = Money(Decimal("1.50"), "BRL")

    result = price.add(tax)

    assert result.amount == Decimal("11.50")
    assert result.currency == "BRL"

def test_money_addition_different_currencies_raises():
    brl = Money(Decimal("10.00"), "BRL")
    usd = Money(Decimal("10.00"), "USD")

    with pytest.raises(CurrencyMismatchError):
        brl.add(usd)

# PASSO 2: GREEN — implementação mínima
@dataclass(frozen=True)
class Money:
    amount: Decimal
    currency: str

    def add(self, other: "Money") -> "Money":
        if self.currency != other.currency:
            raise CurrencyMismatchError()
        return Money(self.amount + other.amount, self.currency)
```

## Estrutura de Testes — Padrão AAA

```python
# tests/unit/domain/test_order.py
import pytest
from decimal import Decimal
from uuid import uuid4
from domain.entities.order import Order
from domain.value_objects.money import Money
from domain.exceptions import OrderNotEditableError

class TestOrderAddItem:
    """Agrupa testes relacionados ao comportamento add_item."""

    def test_add_item_to_pending_order_succeeds(self):
        # Arrange
        order = Order(id=uuid4(), customer_id=uuid4())
        item = build_order_item(price=Money(Decimal("50.00"), "BRL"))

        # Act
        order.add_item(item)

        # Assert
        assert len(order.items) == 1
        assert order.total == Money(Decimal("50.00"), "BRL")

    def test_add_item_to_confirmed_order_raises(self):
        # Arrange
        order = Order(id=uuid4(), customer_id=uuid4())
        order.confirm()  # muda status

        # Act & Assert
        with pytest.raises(OrderNotEditableError) as exc_info:
            order.add_item(build_order_item())

        assert exc_info.value.order_id == order.id
```

## Fixtures — Boas Práticas

```python
# tests/conftest.py
import pytest
from decimal import Decimal
from uuid import uuid4

@pytest.fixture
def customer_id():
    return uuid4()

@pytest.fixture
def pending_order(customer_id):
    return Order(id=uuid4(), customer_id=customer_id)

@pytest.fixture
def confirmed_order(pending_order):
    pending_order.confirm()
    return pending_order

# Factory para evitar repetição
def build_order_item(
    product_id=None,
    price=None,
    quantity=1,
):
    return OrderItem(
        product_id=product_id or uuid4(),
        price=price or Money(Decimal("10.00"), "BRL"),
        quantity=quantity,
    )
```

## Mocks e Fakes — Quando Usar Cada Um

```python
# FAKE — implementação real simplificada (preferível para repositórios)
class InMemoryOrderRepository(OrderRepository):
    def __init__(self):
        self._store: dict[UUID, Order] = {}

    def find_by_id(self, order_id: UUID) -> Order | None:
        return self._store.get(order_id)

    def save(self, order: Order) -> Order:
        self._store[order.id] = order
        return order

# USO em testes de use case
def test_create_order_saves_to_repository():
    repo = InMemoryOrderRepository()
    use_case = CreateOrderUseCase(repo=repo)

    result = use_case.execute(CreateOrderDTO(customer_id=uuid4()))

    assert repo.find_by_id(result.id) is not None

# MOCK — para verificar interações (use com moderação)
from unittest.mock import MagicMock, AsyncMock

def test_create_order_sends_welcome_email():
    repo = InMemoryOrderRepository()
    email_service = MagicMock()
    use_case = CreateOrderUseCase(repo=repo, email_service=email_service)

    use_case.execute(CreateOrderDTO(customer_id=uuid4()))

    email_service.send_confirmation.assert_called_once()
```

## Testes de API FastAPI

```python
# tests/integration/api/test_orders_api.py
import pytest
from fastapi.testclient import TestClient
from httpx import AsyncClient

@pytest.mark.asyncio
async def test_create_order_returns_201(async_client: AsyncClient):
    payload = {"customer_id": str(uuid4()), "items": []}

    response = await async_client.post("/orders/", json=payload)

    assert response.status_code == 201
    data = response.json()
    assert "id" in data
    assert data["status"] == "pending"

@pytest.mark.asyncio
async def test_create_order_with_invalid_data_returns_422(async_client: AsyncClient):
    response = await async_client.post("/orders/", json={})

    assert response.status_code == 422

# conftest.py para API
@pytest.fixture
async def async_client(app):
    async with AsyncClient(app=app, base_url="http://test") as client:
        yield client
```

## Testes de Contrato (Pact)

```python
# tests/contract/test_payment_provider_contract.py
# Garante que o contrato com serviços externos não quebra
import pytest
from pact import Consumer, Provider

pact = Consumer("order-service").has_pact_with(Provider("payment-service"))

def test_payment_service_create_payment():
    expected_response = {
        "id": "pay_123",
        "status": "pending",
        "amount": 100.00,
    }

    (pact
     .given("payment service is available")
     .upon_receiving("a request to create a payment")
     .with_request("POST", "/payments", body={"amount": 100.00, "currency": "BRL"})
     .will_respond_with(201, body=expected_response))

    with pact:
        result = PaymentClient(pact.uri).create(amount=100.00, currency="BRL")
        assert result["status"] == "pending"
```

## Cobertura — Configuração e Requisitos

```ini
# pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"

[tool.coverage.run]
source = ["src"]
omit = ["*/migrations/*", "*/settings/*", "*/conftest.py"]

[tool.coverage.report]
fail_under = 80        # mínimo aceitável
show_missing = true
skip_covered = false
exclude_lines = [
    "pragma: no cover",
    "if TYPE_CHECKING:",
    "raise NotImplementedError",
    "@abstractmethod",
]
```

```bash
# Rodar testes com cobertura
pytest --cov=src --cov-report=term-missing --cov-report=html -v

# Rodar apenas testes rápidos (unit)
pytest tests/unit/ -v

# Rodar com paralelismo
pytest -n auto tests/
```

## Nomenclatura de Testes — Convenção

```
test_<unidade>_<cenário>_<resultado_esperado>

✅ test_order_add_item_to_confirmed_order_raises_not_editable_error
✅ test_money_add_different_currencies_raises_currency_mismatch
✅ test_create_order_use_case_with_out_of_stock_item_raises_insufficient_stock

❌ test_order_1
❌ test_fail
❌ test_create
```

## Anti-Patterns de Teste

| Anti-Pattern | Problema |
|---|---|
| Testar implementação, não comportamento | Frágil a refatoração |
| Teste depende de ordem de execução | Compartilhamento de estado |
| Mock de tudo | Teste não valida nada real |
| Assert único por teste | Não sabe qual falhou |
| Setup enorme antes do assert | Teste difícil de entender |
| Testar métodos privados diretamente | Viola encapsulamento |

