djust docs
Browse documentation

Testing API Reference

On this page
from djust.testing import LiveViewTestClient, SnapshotTestMixin, LiveViewSmokeTest, performance_test

class LiveViewTestClient

Test LiveViews without a browser or WebSocket connection.

LiveViewTestClient(view_class, request_factory=None, user=None)

Parameters:

  • view_class — The LiveView subclass to test
  • request_factory — Optional Django RequestFactory. Creates one if not provided.
  • user — Optional authenticated user to attach to requests.

Methods

mount(via_websocket=True, **params) -> LiveViewTestClient

Initialize the view. Calls mount() on the LiveView. The default via_websocket=True mounts as the WebSocket path does; pass via_websocket=False to exercise the HTTP-prerender branch instead. No login or permission check runs here.

client = LiveViewTestClient(MyView)
client.mount()                        # No URL params
client.mount(item_id=5)               # With URL kwargs
client.mount(page=2, query="test")    # Multiple params

Returns self for chaining.


send_event(event_name, raise_on_missing=True, **params) -> dict

Send an event to the view. Calls the named event handler.

result = client.send_event("increment")
result = client.send_event("search", value="laptop")
result = client.send_event("delete_item", item_id=5)

Parameters:

  • event_name (str) — The handler method name
  • raise_on_missing (bool) — If True (default), raise NoHandlerFoundError when no handler exists for event_name. Pass False to get a {"success": False, ...} result instead.
  • **params — Event parameters passed to the handler

Returns: a dict with the keys success, error, state_before, state_after and duration_ms. The handler's own return value is discarded.

An exception raised inside the handler is captured and reported as success=False with the message in error; it is not raised. Assert result["success"] so a crashing handler fails your test:

result = client.send_event("search", value="laptop")
assert result["success"], result["error"]

assert_state(**expected)

Assert that the view's current state matches the expected values.

client.assert_state(count=3)
client.assert_state(is_valid=True, success_message="Saved!")

Raises AssertionError if any attribute doesn't match.


render() -> str

Render the current state to HTML and return it.

html = client.render()
assert "laptop" in html
assert "<li>" in html

get_state() -> dict

The current view state as a dictionary. Useful for assertions not covered by assert_state():

state = client.get_state()
assert "email" in state["field_errors"]
assert len(state["items"]) == 3

Example: Full Test

from django.test import TestCase
from djust.testing import LiveViewTestClient
from myapp.views import CounterView


class TestCounterView(TestCase):
    def test_initial_state(self):
        client = LiveViewTestClient(CounterView)
        client.mount()
        client.assert_state(count=0)

    def test_increment(self):
        client = LiveViewTestClient(CounterView)
        client.mount()

        client.send_event("increment")
        client.assert_state(count=1)

        client.send_event("increment")
        client.assert_state(count=2)

    def test_renders_count(self):
        client = LiveViewTestClient(CounterView)
        client.mount()
        client.send_event("increment")

        html = client.render()
        self.assertIn("1", html)

    def test_authenticated(self):
        from django.contrib.auth.models import User
        user = User.objects.create_user("test", password="pass")
        client = LiveViewTestClient(ProtectedView, user=user)
        client.mount()
        # `user` is not view state; check the request the view was mounted with
        self.assertEqual(client.view_instance.request.user, user)

class SnapshotTestMixin

Compare rendered HTML against stored snapshot files.

Methods

assert_html_snapshot(name, html)

Normalize html (whitespace, comments) and compare it against a stored snapshot at <test file's directory>/snapshots/{name}.html.snapshot.

On first run, saves the snapshot. Subsequent runs compare against it.

Parameters:

  • name (str) — Snapshot name (no extension)
  • html (str) — HTML to compare

Class attributes:

  • snapshot_dir (str, default "snapshots") — directory, relative to the test file, that holds snapshots
  • update_snapshots (bool, default False) — if True, overwrite stored snapshots instead of comparing
from django.test import TestCase
from djust.testing import LiveViewTestClient, SnapshotTestMixin
from myapp.views import ProductCardView


class TestProductCard(TestCase, SnapshotTestMixin):
    def test_renders_product(self):
        client = LiveViewTestClient(ProductCardView)
        client.mount(product_id=1)

        self.assert_html_snapshot("product_card_default", client.render())

To update all snapshots after an intentional UI change:

UPDATE_SNAPSHOTS=1 pytest

class LiveViewSmokeTest

Auto-discover all LiveView subclasses in an app and run smoke tests.

Class Attributes

AttributeTypeDefaultDescription
app_labelstrNoneOnly test views in this Django app
max_queriesint50Fail if mounting + rendering a view exceeds this many DB queries
fuzzboolTrueSend XSS and type-mismatch payloads to handlers
skip_viewslist[]View classes to skip
view_configdict{}Per-view setup: {ViewClass: {"mount_params": {...}, "user": user}}

Example

from django.test import TestCase
from djust.testing import LiveViewSmokeTest


class TestAllMyAppViews(TestCase, LiveViewSmokeTest):
    app_label = "myapp"
    max_queries = 15
    fuzz = True

This auto-discovers all LiveView subclasses in myapp and:

  1. Mounts each view
  2. Asserts it renders without raising
  3. Checks DB query counts stay under max_queries
  4. If fuzz=True, sends malformed payloads and asserts no uncaught exceptions

@performance_test

Decorator that enforces performance budgets on test methods.

@performance_test(max_time_ms=50, max_queries=3)

Parameters:

  • max_time_ms (float, default 100) — Maximum milliseconds the whole test method may take
  • max_queries (int, default 10) — Maximum DB queries allowed across the whole test method
  • track_memory (bool, default False) — Track memory allocated during the test with tracemalloc
  • max_memory_bytes (int, optional) — Maximum bytes allocated when track_memory=True
from django.test import TestCase
from djust.testing import LiveViewTestClient, performance_test


class TestSearchPerformance(TestCase):
    @performance_test(max_time_ms=50, max_queries=3)
    def test_search_is_fast(self):
        client = LiveViewTestClient(SearchView)
        client.mount()
        client.send_event("search", value="laptop")

Fails if the whole test method — including client setup and mount(), not just search — takes more than 50 ms or runs more than 3 queries.


See Also