04.07 — Politique de couverture : ce qui est testé, ce qui ne l’est pas, pourquoi#

La couverture par ligne est explicitement scopée au DSL pur et à l’infra agnostique. Le reste est testé par d’autres moyens (cram, types, snapshots, e2e) ou hors scope (shapes inertes).

pyproject.toml#tool.coverage.run#

[tool.coverage.run]
omit = [
    "*/__init__.py",
    "*/tests/*",
    "*/test_*.py",
    "src/ocarina/custom_errors/*",
    "src/ocarina/custom_types/*",
    "src/ocarina/ports/*",
    "src/ocarina/opinionated/loggers/custom_types/*",
    "src/**/*singleton.py",
    "src/**/consts/**",
    # Selenium adapter layer — exercised only against a real browser.
    "src/ocarina/infra/selenium/*",
    "src/ocarina/dsl/testing/selenium/*",
    "src/ocarina/opinionated/cli/selenium/*",
    "src/ocarina/pom/selenium/muted.py",
    # Playwright adapter layer — exercised only against a real browser.
    "src/ocarina/infra/playwright/*",
    "src/ocarina/dsl/testing/playwright/*",
    "src/ocarina/opinionated/cli/playwright/*",
    "src/ocarina/pom/playwright/*",
    "src/ocarina/opinionated/cli/phantoms.py",
    # Opinionated loggers — only file_logger.py stays in scope.
    "src/ocarina/opinionated/loggers/create_matching_logger.py",
    "src/ocarina/opinionated/loggers/muted_logger.py",
    "src/ocarina/opinionated/loggers/print_logger.py",
    "src/ocarina/opinionated/loggers/print_and_file_logger.py",
    "src/ocarina/opinionated/loggers/utils/*",
    # Traversed by cram tests
    "src/ocarina/opinionated/cli/store.py",
    "src/ocarina/opinionated/cli/builder.py",
]

Catégories#

1. Exclusions évidentes#

PatternJustification
*/__init__.pyModules vides (Python 3 n’en a souvent pas besoin)
*/tests/*, */test_*.pyLes tests ne se testent pas eux-mêmes

2. Types / errors / ports / shapes#

CheminJustification
src/ocarina/custom_types/*Juste des type X = ... et frozen dataclasses. Pas de logique runtime.
src/ocarina/custom_errors/*Juste des sous-classes d’Exception. Pas de logique.
src/ocarina/ports/*ABCs et Protocols. Pas de comportement.
src/ocarina/opinionated/loggers/custom_types/*id.
src/**/consts/**Constantes (LOGGERS_CHOICES = (...)).
src/**/*singleton.pyWrappers Singleton triviaux.

3. Couches Selenium et Playwright (e2e seulement)#

CheminJustification
src/ocarina/infra/selenium/*Code Selenium réel (Chrome(), Firefox()). Ne s’exécute qu’avec un vrai navigateur.
src/ocarina/dsl/testing/selenium/*create_selenium_test, create_selenium_watcher (factory triviales sur TestWatcher).
src/ocarina/opinionated/cli/selenium/*create_selenium_*_cli_store (lit platform.system, instancie).
src/ocarina/pom/selenium/muted.pyMutedPOM utilitaire (presque vide).
src/ocarina/infra/playwright/*Code Playwright réel + l’acteur PlaywrightDriver. Le test de bout en bout ne se prouve qu’avec un vrai navigateur.
src/ocarina/dsl/testing/playwright/*create_playwright_test, create_playwright_watcher (mêmes factory triviales).
src/ocarina/opinionated/cli/playwright/*create_playwright_cli_store (CLI Playwright).
src/ocarina/pom/playwright/*MutedPlaywrightPOM + mixins (Null Object, presque vide).

Ces fichiers sont couverts par :

  • ocarina-example/e2e.yml (CI manuelle, Firefox + Redis).
  • ocarina-with-ai-example/ai_proof_e2e.yml (CI manuelle, Chrome + Firefox).

Ce sont les suites e2e externes qui prouvent que ces adapters marchent, pas la couverture pytest du framework.

Nuance pour l’acteur Playwright : infra/playwright/* est hors métrique de couverture, mais sa logique de marshallisation est bel et bien exercée par test_playwright_driver_actor.py, qui patche sync_playwright par un mock et tourne en CI sans navigateur. Omis du score ≠ non testé.

4. Loggers opinionated (sauf FileLogger)#

CheminJustification
print_logger.py, muted_logger.py, print_and_file_logger.py, create_matching_logger.py, utils/*Variants triviaux (terminal seul, muted, factory dispatch).
file_logger.pyReste en scope — c’est le seul qui a une vraie logique (taxonomy → arborescence de fichiers, cleanup, recycle).

Le FileLogger est testé par test_loggers_and_reports.py ; les autres sont des variations triviales du PrintLogger que les snapshots couvrent.

5. CLI traversé par cram#

CheminJustification
src/ocarina/opinionated/cli/store.pyTesté par cram (les .t exercent setget_Unset)
src/ocarina/opinionated/cli/builder.pyTesté par cram (les .t exercent parse + validation + effects)
src/ocarina/opinionated/cli/phantoms.pyTesté indirectement (les fields qui l’utilisent passent par cram)

Périmètre retenu#

ModulePourquoi
src/ocarina/railway/*Cœur ROP
src/ocarina/dsl/invariants/*DSL invariants — testé par pytest + mypy plugins + hypothesis
src/ocarina/dsl/testing/* (sauf selenium/)Orchestration — testé dans le dossier “scenarios” des tests unitaires
src/ocarina/dsl/testing_with_railway/*DSL ROP — testé par scenarios + mypy plugins
src/ocarina/infra/drivers_pool.py, driver_builder.py, screenshotter.py, act_counter.pyInfra agnostique — testée avec FakeDriver
src/ocarina/aggregates/*TypeGuard helpers
src/ocarina/custom_invariants/*Invariants pré-faits — testés par “scenarios” et traversés par les classes qui les utilisent
src/ocarina/opinionated/dsl/drive_page.pyAlias trivial
src/ocarina/opinionated/infra/act_counter.pyThread-local counter
src/ocarina/opinionated/launcher/bootstrap.pyBootstrap — testé par test_test_cycle_and_bootstrap.py
src/ocarina/opinionated/plugins/reports/*Plugins — testés par test_loggers_and_reports.py, test_docx_tests_proofs.py, snapshots

Métriques#

--cov-report=html (HTML) + --cov-report=xml:coverage.xml (XML) montrent :

Type de couvertureMétrique
Line coverage% de lignes exécutées au moins une fois
Branch coverage% de branches (if/else) couvertes dans les deux sens

Le flag --cov-branch est activé : Ocarina mesure aussi la couverture par branches, pas juste par lignes (instructions).

HTML#

make serve-htmlcov ouvre htmlcov/index.html dans le navigateur.

  • Vert : couvert.
  • Rouge : non couvert.
  • Jaune : couvert partiellement (branche manquante).

Pourquoi ne pas “tout” tester#

Approche naïvePolitique Ocarina
Tout tester, viser 100% partoutTester ce qui compte, viser un score sain sur ce qui compte
Couvrir les shapes inertes (type Result = ...) → 100% trivialExclure → le 100% reflète vraiment le comportement couvert
Tester les adapters Selenium en CI unit → setup browser à chaque PRTester les adapters en CI e2e manuelle → CI rapide, CI lourde à la demande
Tester les loggers triviaux → boilerplateTester seulement les loggers à logique réelle

En plus du coverage fourni par pytest, on prend aussi en considération le fait que l’on utilise d’autre mécanismes pour vérifier la qualité d’Ocarina (cram, e2e, snapshots, types, tests manuels).