Capítulo 18: Arquitetura de Software em Camadas, Testes Automatizados com Pytest e Auditoria Contínua
Especialização em Sistemas com Python & POO com Type Hints • Capítulo 18
🗺️ Mapa Conceitual do Tópico
flowchart TD
subgraph LAYERS["Arquitetura em Camadas (Separation of Concerns)"]
PRESENTATION["1. Camada de Apresentação (UI / CLI / Rest Controller)"]
APPLICATION["2. Camada de Aplicação (Casos de Uso & Orquestração)"]
DOMAIN["3. Camada de Domínio Puro (Entidades, VOs & Invariantes)"]
INFRA["4. Camada de Infraestrutura (SQLite DAO, Rede & Sockets)"]
end
subgraph PYTEST_ECO["Garantia de Qualidade com Pytest"]
ASSERT["Asserções Nativas com Introspecção Mágica (assert a == b)"]
FIXTURES["Fixtures Injetáveis (@pytest.fixture com Setup / Teardown)"]
PARAM["Testes Parametrizados (@pytest.mark.parametrize)"]
MOCK["Dublês de Teste e Isolamento com unittest.mock"]
end
subgraph AUDIT["Auditoria Contínua & Observabilidade"]
LOGS["Logs Estruturados em JSON Datado (reports/audit.json)"]
COVERAGE["Métricas de Cobertura de Código com pytest-cov"]
end
PRESENTATION --> APPLICATION
APPLICATION --> DOMAIN
APPLICATION --> INFRA
INFRA -.->|Implementa Portas| DOMAIN
PYTEST_ECO --> AUDIT
style LAYERS fill:#ede7f6,stroke:#512da8,stroke-width:2px
style PYTEST_ECO fill:#e1f5fe,stroke:#0288d1,stroke-width:2px
style AUDIT fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
🏛️ 1. Arquitetura em Camadas e Inversão de Controle
A separação de responsabilidades em camadas assegura que as regras essenciais de negócio sobrevivam a mudanças de tecnologias de infraestrutura ou interface gráfica:
- Apresentação (Presentation Layer): Janelas CustomTkinter, controladores web FastAPI ou terminais CLI. Converte eventos de entrada em chamadas aos serviços de aplicação.
- Aplicação (Application Layer / Use Cases): Orquestra fluxos transacionais, autenticação, controle de auditoria e despacho de notificações.
- Domínio (Domain Layer): O núcleo da inteligência de negócio. Contém entidades puras, objetos de valor e exceções customizadas, sem dependências de frameworks externos ou bibliotecas de banco de dados.
- Infraestrutura (Infrastructure Layer): Implementações concretas de acesso a banco de dados (SQLite DAO), sistemas de arquivos (
pathlib), clientes HTTP e filas de mensageria.
🧪 2. O Arsenal Moderno do Pytest
O pytest substituiu amplamente o módulo legado unittest por eliminar o boilerplate de classes derivadas (unittest.TestCase) e fornecer relatórios de falhas com inspeção detalhada de variáveis locais:
Recursos Canônicos do Pytest:
| Recurso | Sintaxe / Mecanismo | Benefício Arquitetural |
|---|---|---|
| Asserções Simples | assert expressao == esperado |
Elimina dezenas de métodos legados (assertEqual, assertTrue). |
| Fixtures Modulares | @pytest.fixture |
Configuração reutilizável com injeção automática de dependências. |
| Teardown Limpo | yield recurso dentro da fixture |
Garante limpeza de bancos e arquivos após o encerramento do teste. |
| Parametrização | @pytest.mark.parametrize |
Executa a mesma lógica com matrizes extensas de entradas e saídas. |
| Captura de Erros | with pytest.raises(TipoExcecao): |
Valida se regras de negócio disparam as exceções corretas. |
import pytest
from decimal import Decimal
@pytest.fixture
def conta_base() -> ContaCorrente:
"""Fixture que fornece uma conta limpa para cada teste."""
return ContaCorrente(numero="12345-6", titular="Auditoria", saldo_inicial=Decimal("1000.00"))
@pytest.mark.parametrize("saque,saldo_esperado", [
(Decimal("100.00"), Decimal("900.00")),
(Decimal("500.00"), Decimal("500.00")),
(Decimal("1000.00"), Decimal("0.00")),
])
def test_saques_validos(conta_base: ContaCorrente, saque: Decimal, saldo_esperado: Decimal) -> None:
sucesso = conta_base.sacar(saque)
assert sucesso is True
assert conta_base.saldo == saldo_esperado
🎭 3. Isolamento com Dublês de Teste (unittest.mock)
Testes unitários não devem tocar a rede física nem alterar bancos de dados em produção. A biblioteca padrão provê o módulo unittest.mock com as ferramentas Mock, MagicMock e @patch:
from unittest.mock import MagicMock
from decimal import Decimal
def test_servico_faturamento_com_mock() -> None:
# Criação de um dublê de repositório sem tocar no SQLite físico
mock_repo = MagicMock(spec=ClienteDAO)
mock_repo.buscar_saldo.return_value = Decimal("5000.00")
servico = FaturamentoService(repositorio=mock_repo)
parecer = servico.processar_cobranca(id_cliente=1, valor=Decimal("200.00"))
assert parecer.aprovado is True
mock_repo.buscar_saldo.assert_called_once_with(1)
📊 4. Auditoria Contínua e Governança de Logs
Em conformidade com normas regulatórias como a LGPD e os critérios de auditoria contínua, sistemas corporativos devem gerar trilhas de auditoria imutáveis (Audit Trails) serializadas em JSON estruturado:
\[\text{reports/YYYY-MM-DD\_HH-MM-SS\_auditoria.json}\]{
"timestamp_utc": "2026-09-28T14:45:00.125Z",
"usuario_executor": "admin.seguranca",
"acao": "REAJUSTE_TAXA_CREDITO",
"status_conformidade": "APROVADO",
"payload_anterior": { "taxa": "0.025" },
"payload_novo": { "taxa": "0.028" },
"hash_integridade_sha256": "8f3b2a..."
}
A execução de suites de testes via pytest --cov=. em pipelines de CI/CD assegura que nenhuma regressão de código chegue ao ambiente de produção.