from django.conf import settings from django.conf.urls import include, url from django.contrib.admin import AdminSite from django.contrib.admin.templatetags.admin_urls import admin_urlname from django.contrib.auth.models import User from django.contrib.messages.middleware import MessageMiddleware from django.test import RequestFactory, TestCase from django.urls import clear_url_caches, reverse, set_urlconf class AdminTestCase(TestCase): """ Testing the admin site """ #: The model to test model = None #: The admin class to test admin_class = None @classmethod def setUpClass(cls): super(AdminTestCase, cls).setUpClass() cls.admin_user = User.objects.create_superuser('admin', 'admin@example.org', password='admin') def setUp(self): super(AdminTestCase, self).setUp() # Have a separate site, to avoid dependency on polymorphic wrapping or standard admin configuration self.admin_site = AdminSite() if self.model is not None: self.admin_register(self.model, self.admin_class) def tearDown(self): clear_url_caches() set_urlconf(None) def register(self, model): """Decorator, like admin.register()""" def _dec(admin_class): self.admin_register(model, admin_class) return admin_class return _dec def admin_register(self, model, admin_site): """Register an model with admin to the test case, test client and URL reversing code.""" self.admin_site.register(model, admin_site) # Make sure the URLs are reachable by reverse() clear_url_caches() set_urlconf(tuple([ url('^tmp-admin/', include(self.admin_site.urls)) ])) def get_admin_instance(self, model): try: return self.admin_site._registry[model] except KeyError: raise ValueError("Model not registered with admin: {}".format(model)) @classmethod def tearDownClass(cls): super(AdminTestCase, cls).tearDownClass() clear_url_caches() set_urlconf(None) def get_add_url(self, model): admin_instance = self.get_admin_instance(model) return reverse(admin_urlname(admin_instance.opts, 'add')) def get_changelist_url(self, model): admin_instance = self.get_admin_instance(model) return reverse(admin_urlname(admin_instance.opts, 'changelist')) def get_change_url(self, model, object_id): admin_instance = self.get_admin_instance(model) return reverse(admin_urlname(admin_instance.opts, 'change'), args=(object_id,)) def get_history_url(self, model, object_id): admin_instance = self.get_admin_instance(model) return reverse(admin_urlname(admin_instance.opts, 'history'), args=(object_id,)) def get_delete_url(self, model, object_id): admin_instance = self.get_admin_instance(model) return reverse(admin_urlname(admin_instance.opts, 'delete'), args=(object_id,)) def admin_get_add(self, model, qs=''): """ Make a direct "add" call to the admin page, circumvening login checks. """ admin_instance = self.get_admin_instance(model) request = self.create_admin_request('get', self.get_add_url(model) + qs) response = admin_instance.add_view(request) self.assertEqual(response.status_code, 200) return response def admin_post_add(self, model, formdata, qs=''): """ Make a direct "add" call to the admin page, circumvening login checks. """ admin_instance = self.get_admin_instance(model) request = self.create_admin_request('post', self.get_add_url(model) + qs, data=formdata) response = admin_instance.add_view(request) self.assertFormSuccess(request.path, response) return response def admin_get_changelist(self, model): """ Make a direct "add" call to the admin page, circumvening login checks. """ admin_instance = self.get_admin_instance(model) request = self.create_admin_request('get', self.get_changelist_url(model)) response = admin_instance.changelist_view(request) self.assertEqual(response.status_code, 200) return response def admin_get_change(self, model, object_id, query=None, **extra): """ Perform a GET request on the admin page """ admin_instance = self.get_admin_instance(model) request = self.create_admin_request('get', self.get_change_url(model, object_id), data=query, **extra) response = admin_instance.change_view(request, str(object_id)) self.assertEqual(response.status_code, 200) return response def admin_post_change(self, model, object_id, formdata, **extra): """ Make a direct "add" call to the admin page, circumvening login checks. """ admin_instance = self.get_admin_instance(model) request = self.create_admin_request('post', self.get_change_url(model, object_id), data=formdata, **extra) response = admin_instance.change_view(request, str(object_id)) self.assertFormSuccess(request.path, response) return response def admin_get_history(self, model, object_id, query=None, **extra): """ Perform a GET request on the admin page """ admin_instance = self.get_admin_instance(model) request = self.create_admin_request('get', self.get_history_url(model, object_id), data=query, **extra) response = admin_instance.history_view(request, str(object_id)) self.assertEqual(response.status_code, 200) return response def admin_get_delete(self, model, object_id, query=None, **extra): """ Perform a GET request on the admin delete page """ admin_instance = self.get_admin_instance(model) request = self.create_admin_request('get', self.get_delete_url(model, object_id), data=query, **extra) response = admin_instance.delete_view(request, str(object_id)) self.assertEqual(response.status_code, 200) return response def admin_post_delete(self, model, object_id, **extra): """ Make a direct "add" call to the admin page, circumvening login checks. """ if not extra: extra = {'data': {'post': 'yes'}} admin_instance = self.get_admin_instance(model) request = self.create_admin_request('post', self.get_delete_url(model, object_id), **extra) response = admin_instance.delete_view(request, str(object_id)) self.assertEqual(response.status_code, 302, "Form errors in calling {0}".format(request.path)) return response def create_admin_request(self, method, url, data=None, **extra): """ Construct an Request instance for the admin view. """ factory_method = getattr(RequestFactory(), method) if data is not None: if method != 'get': data['csrfmiddlewaretoken'] = 'foo' dummy_request = factory_method(url, data=data) dummy_request.user = self.admin_user # Add the management form fields if needed. # base_data = self._get_management_form_data(dummy_request) # base_data.update(data) # data = base_data request = factory_method(url, data=data, **extra) request.COOKIES[settings.CSRF_COOKIE_NAME] = 'foo' request.csrf_processing_done = True # Add properties which middleware would typically do request.session = {} request.user = self.admin_user MessageMiddleware().process_request(request) return request def assertFormSuccess(self, request_url, response): """ Assert that the response was a redirect, not a form error. """ self.assertIn(response.status_code, [200, 302]) if response.status_code != 302: context_data = response.context_data if 'errors' in context_data: errors = response.context_data['errors'] elif 'form' in context_data: errors = context_data['form'].errors else: raise KeyError("Unknown field for errors in the TemplateResponse!") self.assertEqual(response.status_code, 302, "Form errors in calling {0}:\n{1}".format(request_url, errors.as_text())) self.assertTrue('/login/?next=' not in response['Location'], "Received login response for {0}".format(request_url))