Ir para o conteúdo

Testes

Guia completo sobre testes no Global-Data-Finance.


Estrutura de Testes

A árvore de testes espelha cada fonte. Os subdiretórios dentro de cada feature são organizacionais (agrupam por tópico para legibilidade), não arquiteturais — qualquer teste importa diretamente dos módulos da fonte (from globaldatafinance.brazil.<país>.<fonte>.<módulo> import ...).

tests/
├── application/                       # tests do facade público
│   ├── cvm_docs/
│   └── b3_docs/
│       └── result_formatters/
├── brazil/
│   ├── b3_data/
│   │   └── historical_quotes/         # layout plano: 21 test_*.py diretamente na pasta
│   └── cvm/
│       └── fundamental_stocks_data/
│           ├── application/use_cases/ # tests de orquestração (client.py)
│           ├── domain/                # tests de value objects e validators (core.py)
│           ├── infra/adapters/        # tests dos adapters concretos (http.py, extract.py)
│           ├── exceptions/            # tests das exceções (errors.py)
│           └── integration/           # tests integration-marker
├── core/
├── macro_infra/
└── macro_exceptions/

Executando Testes

Todos os Testes

uv run pytest

Com Cobertura

pytest.ini já força fail_under = 70 na cobertura agregada.

uv run pytest --cov=src --cov-report=html

Marcadores

Os markers registrados em pytest.ini são: unit, integration, slow, asyncio (com --strict-markers, então qualquer marker não declarado falha).

# Apenas testes unitários
uv run pytest -m unit

# Apenas testes de integração
uv run pytest -m integration

# Combinar markers
uv run pytest -m "integration and not slow"

Escrevendo Testes

Teste Unitário

import pytest
from globaldatafinance.brazil.cvm.fundamental_stocks_data.core import AvailableDocsCVM
from globaldatafinance.brazil.cvm.fundamental_stocks_data.errors import InvalidDocName

@pytest.mark.unit
class TestAvailableDocs:
    def test_validate_valid_doc(self):
        """Testa validação de documento válido."""
        docs = AvailableDocsCVM()
        docs.validate_docs_name("DFP")  # Não deve lançar exceção

    def test_validate_invalid_doc(self):
        """Testa validação de documento inválido."""
        docs = AvailableDocsCVM()
        with pytest.raises(InvalidDocName):
            docs.validate_docs_name("INVALID")

Tipos e exceções de cada fonte vivem nos módulos da própria fonte: para CVM em brazil.cvm.fundamental_stocks_data.core e brazil.cvm.fundamental_stocks_data.errors; para B3 a divisão é mais granular — entidades em models.py, value objects em years.py/processing.py, validators de filesystem em filesystem.py, asset services em assets.py, exceções em errors.py.

Mocking sem ABC

Como os adapters não são mais ABCs, tests substituem dependências via stub duck-typed ou monkeypatch.setattr:

class MockRepository:
    def download_docs(self, tasks):
        return DownloadResultCVM(
            success_count_downloads=2,
            error_count_downloads=0,
            successful_downloads=["DFP_2023", "ITR_2023"],
            failed_downloads={},
        )

from globaldatafinance.brazil.cvm.fundamental_stocks_data.client import (
    DownloadDocumentsUseCaseCVM,
)

use_case = DownloadDocumentsUseCaseCVM(MockRepository())
result = use_case.execute(destination_path="/tmp/cvm")
assert result.success_count_downloads == 2

Teste de Integração

import pytest
from globaldatafinance import FundamentalStocksDataCVM

@pytest.mark.integration
class TestFundamentalStocksDataIntegration:
    def test_get_available_docs(self):
        """Testa obtenção de documentos disponíveis."""
        cvm = FundamentalStocksDataCVM()
        docs = cvm.get_available_docs()

        assert isinstance(docs, dict)
        assert len(docs) > 0
        assert "DFP" in docs

Fixtures

import pytest
from pathlib import Path

@pytest.fixture
def temp_dir(tmp_path):
    """Cria diretório temporário para testes."""
    return tmp_path

@pytest.fixture
def sample_zip_file(tmp_path):
    """Cria arquivo ZIP de exemplo."""
    zip_path = tmp_path / "test.zip"
    # Criar ZIP...
    return zip_path

Cobertura

Objetivo: >= 70% de cobertura agregada (enforced via fail_under = 70 em pytest.ini). O alvo prático é >= 80% em módulos novos.

# Gerar relatório
uv run pytest --cov=src --cov-report=term-missing

# Relatório HTML
uv run pytest --cov=src --cov-report=html
open htmlcov/index.html

CI/CD

Testes são executados automaticamente em:

  • Push para main ou develop
  • Pull Requests
  • Releases

Veja também: