Skip to main content

pytest-jinja-check

A pytest plugin that lints Jinja2 templates used in FastAPI applications. Catches common issues at test time:

  1. Syntax validation — templates parse without errors
  2. Hardcoded route detection — href="/foo" should be url_for('foo')
  3. Endpoint validation — url_for('endpoint') references actually exist in your app
  4. Context variable checking — routes pass all variables their templates need

Installation

pip install pytest-jinja-check

Or with uv:

uv add pytest-jinja-check

For endpoint validation against a live FastAPI app, install with the fastapi extra:

pip install "pytest-jinja-check[fastapi]"
# or
uv add "pytest-jinja-check[fastapi]"

Quick start

The plugin auto-registers with pytest via entry points. Add configuration to your pyproject.toml:

[tool.pytest-jinja-check]
template_dir = "templates"
python_dir = "app"
auto_check = true
app = "app.main:app"  # optional — enables url_for() endpoint validation

With auto_check = true, all lint checks run at session start — no boilerplate tests required:

pytest

If lint issues are found, pytest exits before your tests run with a single aggregated report.

You can also enable checks for one run without changing config:

pytest --jinja-check

Use --no-jinja-check to skip checks when auto_check is enabled in config.

Manual checks with fixtures

If you prefer explicit tests or need to validate against a specific app instance, use the provided fixtures:

# tests/test_templates.py

def test_template_syntax(template_syntax_errors):
    assert not template_syntax_errors, "\n".join(str(e) for e in template_syntax_errors)

def test_no_hardcoded_routes(hardcoded_routes):
    assert not hardcoded_routes, "\n".join(str(e) for e in hardcoded_routes)

def test_context_variables(missing_context_variables):
    assert not missing_context_variables, "\n".join(
        str(e) for e in missing_context_variables
    )

def test_url_for_endpoints(validate_endpoints):
    from myapp.main import app
    errors = validate_endpoints(app)
    assert not errors, "\n".join(str(e) for e in errors)

Fixtures

All analysis fixtures are session-scoped (run once per test session).

Fixture Returns Description
template_linter_config LinterConfig Resolved configuration
template_variables dict[str, TemplateInfo] Template name -> extracted variables (with inheritance)
route_contexts list[RouteContext] All TemplateResponse calls found via AST
template_syntax_errors list[LintError] Templates that fail to parse
hardcoded_routes list[LintError] Hardcoded URLs that should use url_for()
missing_context_variables list[LintError] Routes missing required template variables
validate_endpoints callable(app) -> list[LintError] Factory: validates url_for() refs against a live app

Configuration

All settings in [tool.pytest-jinja-check] in your pyproject.toml:

Key Default Description
template_dir "templates" Template directory relative to project root
python_dir "." Directory to scan for Python route files
route_file_patterns ["**/*.py"] Glob patterns for route files
ignore_variables ["request", "url_for", ...] Variables to skip (framework-provided)
allowed_url_prefixes ["#", "http://", ...] URL prefixes that aren't hardcoded routes
auto_check false Run all lint checks at pytest session start
app (none) Import string for FastAPI app (e.g. "app.main:app") — enables endpoint validation in auto-check mode

You can also pass pytest CLI options:

Flag Description
--jinja-check Run lint checks for this session (overrides auto_check = false)
--no-jinja-check Skip lint checks (overrides auto_check = true)
--template-lint-config <path> Directory containing pyproject.toml with [tool.pytest-jinja-check] config

Programmatic API

The analysis functions can also be used outside of pytest:

from pytest_jinja_check import (
    analyze_all_templates,
    check_syntax,
    check_hardcoded_routes,
    check_context_variables,
    validate_url_for_references,
    extract_all_route_contexts,
    run_all_checks,
    load_config,
)
from pathlib import Path

# Run all checks in one call (same as auto-check mode)
config = load_config(Path("."))
errors = run_all_checks(config)

# Or run checks individually
# Analyze templates
templates = analyze_all_templates(Path("templates"))
for name, info in templates.items():
    print(f"{name}: needs {info.variables}")

# Find issues
syntax_errors = check_syntax(Path("templates"))
hardcoded = check_hardcoded_routes(Path("templates"))
routes = extract_all_route_contexts(Path("app"))
missing = check_context_variables(Path("templates"), routes)

How it works

  • Template analysis uses jinja2.meta.find_undeclared_variables() on parsed ASTs, recursively resolving {% extends %} to capture inherited variable requirements.
  • Route analysis uses Python's ast module to find TemplateResponse(...) calls and extract template names and context dict keys. Handles both the old positional API and newer keyword API.
  • Endpoint validation introspects a live FastAPI app.routes to get registered endpoint names.

Known limitations

  • Template variables from dynamic {% extends variable %} can't be resolved.
  • Context dicts built outside the TemplateResponse(...) call (e.g., ctx = {...}; TemplateResponse("t.html", ctx)) are flagged as dynamic and skipped rather than producing false positives.
  • The variable union from template inheritance may over-report when a child block completely replaces a parent block that used a variable.

License

MIT

Release files for pytest-jinja-check 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-jinja-check 1.1.0
File Size Uploaded
pytest_jinja_check-1.1.0.tar.gz 50.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-jinja-check 1.1.0
File Interpreter ABI Platform
pytest_jinja_check-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 66.5 kB

Release files / pytest_jinja_check-1.1.0.tar.gz

Download URL pytest_jinja_check-1.1.0.tar.gz
Size 50.6 kB
Tags Source
SHA-256 checksum
How to use checksums
4df166a69113aa557763014ada041a94f97403e9160b75c6ab2d413b01aca787
BLAKE2b-256 checksum
How to use checksums
b132ed90d7ab256c264ffcaabca269354b448486f2d97ce687555edda5a778a8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 23, 2026.

Transparency log

Release files / pytest_jinja_check-1.1.0-py3-none-any.whl

Download URL pytest_jinja_check-1.1.0-py3-none-any.whl
Size 15.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8f89c41fc88772608ca8ef3d92ad54dc53d79dcb50a5106f02abd8cf04ad01eb
BLAKE2b-256 checksum
How to use checksums
4e367be3b6f7c4c51c47eb034444b972766c639cb0889c9ba47a1ad7b4093ef0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 23, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page