Testing the admin - part 2

Series: Admin Testing

  1. Testing the admin
  2. Testing the admin - part 2

As promised, here is the TestCase to test most model views in the apps you specify in the local_apps list.

The test case uses subTests to check each add and changelist views, and it ought to be pretty simple to add the other view types as required.

from django.contrib import admin
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse
from django.utils.encoding import force_text


class TestAdminViews(TestCase):
    # this could be pulled from settings depending on how you decided
    # to structure INSTALLED_APPS
    local_apps = [
        'core',
        'app1'
        'app2'
    ]
    def test_admin_views(self):
        self.user = get_user_model().objects.create(username='test', is_superuser=True)
        self.user.set_password('test')
        self.client.login(username=self.user.username, password="test")

        local_models = (
            k
            for k in admin.site._registry
            if k.__module__ in self.local_apps
        )
        for model in local_models:
            with self.subTest(model=model):
                self.admin_add_view(model)
                self.admin_change_view(model)

    def admin_add_view(self, model):
        if admin.site._registry[model].has_add_permission(
            type(str("request"), (), {"user": self.user})
        ):
            url = reverse(
                "admin:{0}_{1}_add".format(
                    model._meta.app_label, model._meta.model_name
                )
            )
            self.assert_admin_valid_view(model, url)

    def admin_change_view(self, model):
        url = self.reverse(
            "admin:{0!s}_{1!s}_changelist".format(
                model._meta.app_label, model._meta.model_name
            )
        )
        self.assert_admin_valid_view(model, url)

    def assert_admin_valid_view(self, model, url):
        response = self.client.get(url, follow=True)
        self.assertEqual(
            response.status_code,
            200,
            "{0!s} != {1!s} -> {2!s}, url: {3!s}".format(
                response.status_code, 200, repr(model), url
            ),
        )
        self.assertFalse(
            "this_is_the_login_form" in force_text(response.content),
            "login requested for {0!s}".format(force_text(model)),
        )

A quick breakdown of how this TestCase works, there are three types of methods in this class:

First we have the actual test method (test_admin_views). This iterates over each model and using subTest tests each view. Next we have a method for each view. These methods deal with generating the correct url then passing the model and url to the final method type. This final method is an assert helper which makes the request with the client and then asserts the response.

While preparing this post, there are likely some improvements to be made, but this is an excellent starting point to prevent admin misconfigurations getting into production.