Testing Strategy¶
Comprehensive guide detailing automated test organization, execution commands, and testing patterns implemented within Global-Data-Finance.
Test Repository Hierarchy¶
The test directory structure cleanly mirrors the source tree. Subdirectories inside individual source test folders serve an strictly organizational purpose (grouping test scripts by functional topic to enhance code legibility) rather than architectural enforcement—all testing fixtures import symbols directly from flat source modules (from globaldatafinance.brazil.<country>.<source>.<module> import ...).
tests/
├── application/ # Unit tests evaluating public application facades
│ ├── cvm_docs/
│ └── b3_docs/
│ └── result_formatters/
├── brazil/
│ ├── b3_data/
│ │ └── historical_quotes/ # Flat layout containing 21 test_*.py scripts directly in folder
│ └── cvm/
│ └── fundamental_stocks_data/
│ ├── application/use_cases/ # Orchestration use case tests (client.py)
│ ├── domain/ # Value object and domain validator tests (core.py)
│ ├── infra/adapters/ # Concrete I/O adapter tests (http.py, extract.py)
│ ├── exceptions/ # Custom exception triggering suites (errors.py)
│ └── integration/ # Integration test suites marked with @pytest.mark.integration
├── core/
├── macro_infra/
└── macro_exceptions/
Executing Tests¶
Running the Entire Suite¶
uv run pytest
Evaluating Code Coverage¶
The root pytest.ini file structurally enforces a coverage floor of fail_under = 70 across aggregated runs.
uv run pytest --cov=src --cov-report=html
Utilizing Pytest Markers¶
Registered markers in pytest.ini include: unit, integration, slow, and asyncio (enforced via --strict-markers to catch undeclared custom markers immediately).
# Execute only rapid isolated unit tests
uv run pytest -m unit
# Execute integration tests
uv run pytest -m integration
# Combine logical marker constraints
uv run pytest -m "integration and not slow"
Authoring Tests¶
Unit Test Pattern¶
import pytest
from globaldatafinance.brazil.cvm.fundamental_stocks_data.core import AvailableDocsCVM
from globaldatafinance.brazil.cvm.fundamental_stocks_data.errors import (
InvalidDocumentName,
)
@pytest.mark.unit
class TestAvailableDocs:
def test_validate_valid_doc(self):
"""Verifies validation succeeds without exception on valid document code."""
docs = AvailableDocsCVM()
docs.validate_docs_name("DFP") # Should pass silently
def test_validate_invalid_doc(self):
"""Verifies InvalidDocumentName is raised upon supplying unverified strings."""
docs = AvailableDocsCVM()
with pytest.raises(InvalidDocumentName):
docs.validate_docs_name("INVALID_CODE")
Types and exceptions belong within the flat modules of their owning source feature: for CVM inside
brazil.cvm.fundamental_stocks_data.coreandbrazil.cvm.fundamental_stocks_data.errors; for B3 logical separation is segmented topically—entities inmodels.py, value objects inyears.py/processing.py, path security validators infilesystem.py, asset services inassets.py, and exceptions inerrors.py.
Mock and Dependency Substitution Strategies¶
To decouple unit verification from live network servers or real storage file system IO, test fixtures substitute external adapters using duck-typed stubs or 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
Integration Testing Pattern¶
import pytest
from globaldatafinance import FundamentalStocksDataCVM
@pytest.mark.integration
class TestFundamentalStocksDataIntegration:
def test_get_available_docs(self):
"""Evaluates live instantiation and catalog mapping generation."""
cvm = FundamentalStocksDataCVM()
docs = cvm.get_available_docs()
assert isinstance(docs, dict)
assert len(docs) > 0
assert "DFP" in docs
Standard Test Fixtures¶
import pytest
from pathlib import Path
@pytest.fixture
def temp_dir(tmp_path):
"""Provides an isolated temporary directory per test execution."""
return tmp_path
@pytest.fixture
def sample_zip_file(tmp_path):
"""Creates an automated mockup ZIP archive file fixture."""
zip_path = tmp_path / "test.zip"
# Construct mockup ZIP structural headers...
return zip_path
Coverage Goals & Gates¶
Target: >= 70% aggregated code coverage (automatically enforced via fail_under = 70 within pytest.ini). The practical recommended target for newly introduced feature modules is >= 80%.
# Generate terminal missing line summary report
uv run pytest --cov=src --cov-report=term-missing
# Compile interactive HTML diagnostic coverage tree
uv run pytest --cov=src --cov-report=html
open htmlcov/index.html
Continuous Integration (CI/CD)¶
Automated quality verification gates execute on GitHub Actions during:
- Commit pushes targeting
mainordevelopbranches - Every Pull Request opened against project target branches
- Tagged release build packaging workflows
Next Steps¶
- Contributing Guide - Workflow expectations for contributors
- Architecture Guide - Deep structural design concepts