Testing code that uses registries

django_stratagem.testing provides context managers that keep the global, mutable registry state isolated between tests.

Register an implementation for one test

from django_stratagem.testing import temporary_implementation

from myapp.registry import NotificationRegistry
from myapp.test_doubles import FakeChannel  # has a `slug`


def test_uses_fake_channel():
    with temporary_implementation(NotificationRegistry, FakeChannel):
        channel = NotificationRegistry.get(slug=FakeChannel.slug)
        assert channel.send("hi")

Force a conditional implementation on or off

from django_stratagem.testing import override_availability

from myapp.channels import WebhookChannel


def test_webhook_path():
    with override_availability(WebhookChannel, available=True):
        assert WebhookChannel.is_available({}) is True

Isolate all registry state in a fixture

Wrap a fixture so any registries defined or mutated in a test are rolled back:

import pytest

from django_stratagem.testing import isolate_registries


@pytest.fixture(autouse=True)
def _isolate_stratagem():
    with isolate_registries():
        yield

Assert registry state

django_stratagem.testing ships assertion helpers so tests read clearly and fail with useful messages:

from django_stratagem.testing import (
    assert_available,
    assert_choices,
    assert_not_available,
    assert_not_registered,
    assert_registered,
)

from myapp.registry import NotificationRegistry
from myapp.channels import WebhookChannel


def test_registry_state():
    assert_registered(NotificationRegistry, "email")
    assert_not_registered(NotificationRegistry, "carrier_pigeon")
    # Choice slugs in priority order:
    assert_choices(NotificationRegistry, ["email", "sms", "push"])
    assert_available(WebhookChannel, {"user": some_user})
    assert_not_available(WebhookChannel, {})

A failing assert_registered lists the registry’s currently registered slugs, so a typo is easy to spot.

Pytest plugin

Installing django-stratagem registers a pytest11 plugin via its entry point, so the stratagem_isolation fixture is available with no conftest wiring. Request it in any test that defines or mutates registries:

def test_with_isolated_registries(stratagem_isolation):
    class TempChannel(NotificationInterface):
        slug = "temp"

    NotificationRegistry.register(TempChannel)
    assert_registered(NotificationRegistry, "temp")
    # The registration is rolled back automatically when the test ends.

The fixture is opt-in (never autouse), so suites that intentionally mutate global registry state are unaffected unless they request it. It is the shipped equivalent of the hand-written isolate_registries fixture shown above.

Notes and limitations

  • temporary_implementation restores a registry’s implementations, not registry membership. Defining a new Registry subclass adds it to the global list at class-creation time; use isolate_registries to roll that back.

  • These helpers mutate process-global registry state and are not thread-safe. Use them within a single test process (the usual pytest model).