django-admin-tests
Automatic admin smoke-test coverage for Django projects.
Every ModelAdmin you register gets its changelist, add and change views
asserted to return 200 — as part of your test run, under either
manage.py test or pytest. Admin pages break quietly: a renamed field in
list_display, a get_queryset that blows up on a related lookup, a
permission tweak. This catches that without you writing a test per model.
You get one test method per model and view, named after the model, so a broken admin fails under its own name rather than inside a shared loop:
test_admin_smoke_shop_product_changelist
test_admin_smoke_shop_product_add
test_admin_smoke_shop_product_change
test_admin_smoke_blog_article_changelist
...
Install
pip install django-admin-tests
Requires Python 3.10+ and Django 4.2+.
Usage
There are two ways in. Both run under either test runner.
Option 1 — subclass it (works everywhere)
Put this in any test module your runner already collects:
from django_admin_tests.testcases import AdminSmokeTestCase
class AdminSmokeTest(AdminSmokeTestCase):
pass
That's it. Subclassing is also how you customize behavior:
from django_admin_tests.testcases import AdminSmokeTestCase
from myapp.models import InternalReport, LegacyThing
class AdminSmokeTest(AdminSmokeTestCase):
# Admins that intentionally deny access:
model_allowed_status_codes = {InternalReport: {403}}
# Admins to skip entirely:
excluded_models = {LegacyThing}
Note: import the module, not the class, if you'd rather the base class not be collected as a test in its own right. Test discovery scans the whole module namespace for
TestCasesubclasses, sofrom django_admin_tests.testcases import AdminSmokeTestCasemakes the un-customized base class run too.from django_admin_tests import testcasesand thentestcases.AdminSmokeTestCaseavoids that.
Option 2 — pytest plugin (no test file needed)
If you use pytest, the bundled plugin can collect the smoke tests without you writing anything. It's opt-in — installing this package will never silently add tests to your suite:
# pyproject.toml
[tool.pytest.ini_options]
django_admin_tests_auto = true
Since there's no class to subclass in this mode, configure it through Django settings instead (see below).
Settings
| Setting | Default | Purpose |
|---|---|---|
ADMIN_TESTS_ALLOWED_STATUS_CODES |
{200} |
Globally accepted response statuses |
ADMIN_TESTS_MODEL_ALLOWED_STATUS_CODES |
{} |
Per-model overrides, keyed "app_label.ModelName" |
ADMIN_TESTS_EXCLUDE |
[] |
Models to skip entirely, 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.
Excluded models keep their test methods and report as skipped, so an opt-out stays visible in your output rather than looking like a model that was never covered.
Running one model's tests
Because each model gets its own methods, you can iterate on a single admin without re-running the rest:
pytest -k shop_product
python manage.py test myapp.tests.AdminSmokeTest.test_admin_smoke_shop_product_changelist
You can also override a single model's check by defining a method with the generated name — yours wins, and the generated one isn't installed:
class AdminSmokeTest(testcases.AdminSmokeTestCase):
def test_admin_smoke_shop_product_changelist(
self,
): ... # your own assertions for this one admin
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:
- A factory you registered for that model
- Any existing row
- A minimal instance built automatically from the model's fields
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:
# In your AppConfig.ready(), conftest.py, or anywhere that runs at startup
from django_admin_tests import register_factory
from myapp.models import Tricky
def make_tricky():
return Tricky.objects.create(...)
register_factory(Tricky, make_tricky)
The auto-builder is in-house and dependency-free — installing this package pulls in nothing but Django.
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"
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(AdminSmokeTestCase):
user_factory = staticmethod(my_superuser_factory)
Custom admin sites
from myproject.admin import my_site
class AdminSmokeTest(AdminSmokeTestCase):
admin_site = my_site
Known limitations
- Models whose change view can't be instantiated are skipped with a warning rather than failed. That's deliberate — a smoke test shouldn't fail because a model is awkward to construct — but it does mean an un-covered model can go unnoticed. The warning names it.
- The auto-builder doesn't try to satisfy custom
save()/clean()requirements. Useregister_factoryfor those. - Custom
AUTH_USER_MODELsupport is theuser_factoryhook only; there's no auto-detection of required fields. - Many-to-many fields aren't populated (they aren't needed for the views to render).
- The admin registry is read when the test class is created, so admins registered after that aren't covered. In practice registration happens during app loading, which always precedes test module import.
manage.py test --parallelwon't spread these across processes. Django distributes work perTestCaseclass, and all the generated methods live on one class, so the run stays single-process.- Two models whose app label and model name differ only in where the
underscore falls (
foo_bar.bazvsfoo.bar_baz) would generate the same method name. That raisesImproperlyConfiguredrather than silently dropping one — exclude one of them to proceed.
Releasing
Versions are derived from git tags via hatch-vcs — nothing to bump in
pyproject.toml. To cut a release:
- In
CHANGELOG.md, retitle## [Unreleased]to## [X.Y.Z] - YYYY-MM-DDand add a fresh empty## [Unreleased]section above it. - Commit that change, then tag the commit with the bare version — no
vprefix — and push both:git tag X.Y.Z && git push origin master --tags. - Pushing the tag triggers
.github/workflows/release.yml, which builds the sdist/wheel, publishes to PyPI (via Trusted Publishing), and creates a GitHub Release using that CHANGELOG section as its notes.
License
MIT — see LICENSE.
Metadata
Release files for django-admin-tests 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| django_admin_tests-0.2.0.tar.gz | 30.3 kB | With provenance | Details for django_admin_tests-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| django_admin_tests-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for django_admin_tests-0.2.0-py3-none-any.whl |
Total release size: 47.2 kB
Release files / django_admin_tests-0.2.0.tar.gz
| Download URL | django_admin_tests-0.2.0.tar.gz |
|---|---|
| Size | 30.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f4bdd1ad45dc3370093d842174e71bef5dcdb5a512aa188556f68b2315cf7b23
|
|
BLAKE2b-256 checksum How to use checksums |
e93ad61486b3ab99bea4d2e13336c4a93c883eb3faaf9f20ba2f1a909cee882c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Aug 18, 2026.
Transparency logRelease files / django_admin_tests-0.2.0-py3-none-any.whl
| Download URL | django_admin_tests-0.2.0-py3-none-any.whl |
|---|---|
| Size | 17.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7114222074e416425fe1037ee7b848af772204cc8ac92d0967bb22237b5f9275
|
|
BLAKE2b-256 checksum How to use checksums |
98be5f179669348f21daec54b1215c0123ee4d1e590e384ed6f5c8f3b3b194d7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Aug 18, 2026.
Transparency logSecurity for django-admin-tests 0.2.0
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for django_admin_tests-0.2.0.tar.gz
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 Aug 18, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.