Test a component in isolation#
Problem#
You want pytest to render one component and assert on its HTML without standing up a full page, a route, or an HTTP request.
Solution#
Use render_component_by_name from next.testing.
It resolves a component by name as seen from a given template path, renders it with a context mapping you supply, and returns the HTML string.
Walkthrough#
Load the components#
Component discovery is a side effect, so import the components before a test resolves one.
A session-scoped pytest fixture calls eager_load_components once.
import pytest
from next.testing import eager_load_components
@pytest.fixture(autouse=True, scope="session")
def _load_components() -> None:
eager_load_components()
eager_load_components covers the roots configured through COMPONENT_BACKENDS.
Component folders inside a page tree register during the URL router walk instead, which runs when the URLconf first loads.
A suite whose other tests issue NextClient requests has already triggered the walk.
A suite that renders components without any HTTP triggers it by reversing one route in the same fixture, for example with page_reverse() from next.urls.
Render the component#
render_component_by_name takes the component name and the at path the component is referenced from.
The at path drives visibility, so pass a template inside the page tree that can see the component.
The context mapping fills the values the component template reads.
from next.testing import render_component_by_name
def test_info_card_renders_title() -> None:
html = render_component_by_name(
"info_card",
at="notes/pages/template.djx",
context={"title": "Quick start", "subtitle": "Read this first"},
)
assert "Quick start" in html
assert "info-card" in html
The helper raises LookupError when no visible component matches the name from the at path.
Assert on the markup#
The return value is a plain string, so any HTML assertion works.
The assert_has_class and find_anchor helpers from next.testing keep class and link checks readable.
from next.testing import assert_has_class, find_anchor, render_component_by_name
def test_info_card_marks_the_root() -> None:
html = render_component_by_name(
"info_card",
at="notes/pages/template.djx",
context={"title": "Quick start"},
)
assert_has_class(html, "info-card")
def test_info_card_links_to_detail() -> None:
html = render_component_by_name(
"info_card",
at="notes/pages/template.djx",
context={"title": "Quick start", "href": "/notes/1/"},
)
anchor = find_anchor(html, href="/notes/1/", text="Quick start")
assert 'href="/notes/1/"' in anchor
Pass a request when the component needs one#
When a component callable reads the request, build one with RequestFactory and pass it through the request keyword.
from django.test import RequestFactory
from next.testing import render_component_by_name
def test_user_badge_shows_username(db, django_user_model) -> None:
user = django_user_model.objects.create_user("ada")
request = RequestFactory().get("/")
request.user = user
html = render_component_by_name(
"user_badge",
at="notes/pages/template.djx",
request=request,
)
assert "ada" in html
Verification#
Run the component tests.
uv run pytest -k component
Every test passes. Each component rendered on its own, with no page or route involved.
See also#
See also
Testing for the testing toolkit. Build a composite component for the component under test.