django-admin-tests

Automatic admin smoke-test coverage for Django projects. Better than nothing.

Why

Admin pages break quietly. A renamed field in list_display, a get_queryset that blows up on a related lookup, a permission tweak that locks out the wrong people — none of it shows up until someone opens the page.

This package asserts that every ModelAdmin you register still serves its changelist, add and change views, as part of your test run, under either manage.py test or pytest. No test per model.

Install

pip install django-admin-tests

Requires Python 3.10+ and Django 4.2+. The only runtime dependency is Django itself.

Quickstart

Put this in any test module your runner already collects:

from django_admin_tests import testcases


class AdminSmokeTest(testcases.AdminSmokeTestCase):
    pass

That's the whole setup. Run your tests as usual:

pytest
# or
python manage.py test

The examples import the module, not the class, on purpose. Test discovery scans the whole module namespace for TestCase subclasses, so importing the name directly collects the un-customized base class as a test of its own, alongside your subclass:

from django_admin_tests.testcases import AdminSmokeTestCase  # base class runs too


class AdminSmokeTest(AdminSmokeTestCase):
    pass

Customizing

Subclassing is also how you adjust behavior:

from django_admin_tests import testcases

from myapp.models import InternalReport, LegacyThing


class AdminSmokeTest(testcases.AdminSmokeTestCase):
    # Admins that intentionally deny access:
    model_allowed_status_codes = {InternalReport: {403}}
    # Admins to skip entirely:
    excluded_models = {LegacyThing}
AttributeDefaultPurpose
admin_siteadmin.siteTest a custom AdminSite
allowed_status_codes{200}Globally accepted statuses
model_allowed_status_codes{}Per-model status overrides
excluded_modelsNoneModels to skip entirely
user_factoryNoneSupply the authenticating user

pytest plugin

If you use pytest, the bundled plugin can collect the smoke tests without you writing a test file at all. It is opt-in — installing this package will never silently add tests to your suite:

# pyproject.toml
[tool.pytest.ini_options]
django_admin_tests_auto = true

There's no class to subclass in this mode, so configure it through Django settings instead.

Settings

SettingDefaultPurpose
ADMIN_TESTS_ALLOWED_STATUS_CODES{200}Globally accepted statuses
ADMIN_TESTS_MODEL_ALLOWED_STATUS_CODES{}Per-model overrides, keyed "app_label.ModelName"
ADMIN_TESTS_EXCLUDE[]Models to skip, same key format
# settings.py
ADMIN_TESTS_MODEL_ALLOWED_STATUS_CODES = {"myapp.InternalReport": [403]}
ADMIN_TESTS_EXCLUDE = ["myapp.LegacyThing"]

Resolution order is class attribute → Django setting → built-in default, so a subclass always wins over settings.

Change views need an object

To load a change view, there has to be something to change. An instance is resolved per model in this order:

If none of those work — most often a required self-referential or circular foreign key — that model's change-view check is skipped with a warning, not failed. Register a factory to cover it:

from django_admin_tests import register_factory

from myapp.models import Tricky


def make_tricky():
    return Tricky.objects.create(...)


register_factory(Tricky, make_tricky)

Call it anywhere that runs at startup — an AppConfig.ready(), or conftest.py. The auto-builder is written in-house and pulls in no third-party dependency.

Custom user models

The test client authenticates as a superuser it creates itself, using the default User fields. If your AUTH_USER_MODEL needs something else, supply a factory:

class AdminSmokeTest(testcases.AdminSmokeTestCase):
    user_factory = staticmethod(my_superuser_factory)

Turning it off

The tests are tagged django_admin_tests:

python manage.py test --exclude-tag=django_admin_tests
pytest -m "not django_admin_tests"

Known limitations