Bengal maintains high code quality through a multi-layered testing strategy with 4,000+ tests.
Test Layers
Fast & Focused (tests/unit/)
Test individual classes/functions. Mock external deps.
Target: 90%+ coverage.
Workflows (tests/integration/)
Test subsystem interactions and build flows. Verify cache.
Performance (benchmarks/)
Measure build speed, memory, incremental builds. Entry points:test_build.py, test_cold_build_permutations.py, test_nav_tree_performance.py.
Quick Start
# Fast feedback loop (recommended during development)
pytest -m "not slow" -n auto # ~20 seconds
# Unit tests only
pytest tests/unit -n auto # ~8 seconds
# Full suite
pytest # ~60 seconds
Test Infrastructure
Test Roots
Minimal, reusable site structures intests/roots/:
| Root | Purpose | Pages |
|---|---|---|
test-basic |
Minimal smoke test | 1 |
test-directives |
Card, admonition, glossary | 4+ |
test-navigation |
Multi-level hierarchy | 8 |
test-large |
Performance testing | 100+ |
@pytest.mark.bengal(testroot="test-directives")
def test_card_directive(site, build_site):
build_site()
assert "card-grid" in (site.output_dir / "cards/index.html").read_text()
Canonical Mocks
Use shared mocks instead of inline class definitions:
from tests._testing.mocks import MockPage, MockSection, MockSite
page = MockPage(title="Test", url="/test/", tags=["python"])
site = MockSite(pages=[page])
Module-Scoped Fixtures
Rendering tests use module-scoped parser for efficiency:
# In tests/unit/rendering/
def test_markdown(parser): # Parser created once per module
result = parser.parse("# Hello", {})
assert "<h1>" in result
Key Testing Patterns
1. Declarative Test Sites
@pytest.mark.bengal(testroot="test-basic", confoverrides={"site.title": "Custom"})
def test_with_overrides(site):
assert site.title == "Custom"
2. Property-Based Testing
For critical utilities, we usehypothesisto test edge cases (116 tests, 11,600+ examples).
3. Parallel Safety
Mark tests with internal parallelism:
@pytest.mark.parallel_unsafe # Prevents xdist worker crashes
def test_concurrent_operations():
with ThreadPoolExecutor() as ex:
...
Incremental Build Invariants
Incremental behavior is verified with invariant tests that exercise real build flows
without mocks. Seetests/integration/test_incremental_invariants.pyfor:
- Unchanged builds skip all pages
- Single-page content edits rebuild only that page
- Section updates propagate to parents
- Cross-process cache stability
These tests protect the provenance-based detection pipeline from regressions and should be updated alongside changes to incremental build logic.
Continuous Integration
Every PR runs:
- Linting:
ruffcheck and format - Type Checking:
tytype checker - Test Suite: Unit + integration (
-m "not slow") - Full Suite: On main/release branches
Seetests/README.mdfor complete documentation.