Testing custom admin actions in django
Here is how you do it with login and everything, a complete test case:
from django.test import TestCase
from django.urls import reverse
from content_app.models import Content
class ContentModelAdminTests(TestCase):
def setUp(self):
# Create some object to perform the action on
self.content = Content.objects.create(titles='{"main": "test tile", "seo": "test seo"}')
# Create auth user for views using api request factory
self.username = 'content_tester'
self.password = 'goldenstandard'
self.user = User.objects.create_superuser(self.username, '[email protected]', self.password)
def shortDescription(self):
return None
def test_actions1(self):
"""
Testing export_as_json action
App is content_app, model is content
modify as per your app/model
"""
data = {'action': 'export_as_json',
'_selected_action': [self.content._id, ]}
change_url = reverse('admin:content_app_content_changelist')
self.client.login(username=self.username, password=self.password)
response = self.client.post(change_url, data)
self.client.logout()
self.assertEqual(response.status_code, 200)
Just modify to use your model and custom action and run your test.
UPDATE: If you get a 302, you may need to use follow=True
in self.client.post()
.
Just pass the parameter action
with the action name.
response = client.post(change_url, {'action': 'mark_as_read', ...})
Checked items are passed as _selected_action
parameter. So code will be like this:
fixtures = [MyModel.objects.create(read=False),
MyModel.objects.create(read=True)]
should_be_untouched = MyModel.objects.create(read=False)
#note the unicode() call below
data = {'action': 'mark_as_read',
'_selected_action': [unicode(f.pk) for f in fixtures]}
response = client.post(change_url, data)
Note that even if the POST is successful, you still need to test that your action performed the operations intended successfully.
Here's another method to test the action directly from the Admin class:
from django.contrib.auth.models import User
from django.contrib.admin.sites import AdminSite
from django.shortcuts import reverse
from django.test import RequestFactory, TestCase
from django.contrib.messages.storage.fallback import FallbackStorage
from myapp.models import MyModel
from myapp.admin import MyModelAdmin
class MyAdminTestCase(TestCase):
def setUp(self) -> None:
self.site = AdminSite()
self.factory = RequestFactory()
self.superuser = User.objects.create_superuser(username="superuser", is_staff=True)
def test_admin_action(self):
ma = MyModelAdmin(MyModel, self.site)
url = reverse("admin:myapp_mymodel_changelist")
superuser_request = self.factory.get(url)
superuser_request.user = self.superuser
# if using 'messages' in your actions
setattr(superuser_request, 'session', 'session')
messages = FallbackStorage(superuser_request)
setattr(superuser_request, '_messages', messages)
qs = MyModel.objects.all()
ma.mymodeladminaction(superuser_request, queryset=qs)
# check that your action performed the operations intended
...
This is what I do:
data = {'action': 'mark_as_read', '_selected_action': Node.objects.filter(...).values_list('pk', flat=True)}
response = self.client.post(reverse(change_url), data, follow=True)
self.assertContains(response, "blah blah...")
self.assertEqual(Node.objects.filter(field_to_check=..., pk__in=data['_selected_action']).count(), 0)
A few notes on that, comparing to the accepted answer:
- We can use
values_list
instead of list comprehension to obtain the ids. - We need to specify
follow=True
because it is expected that a successfull post will lead to a redirect - Optionally assert for a successful message
- Check that the changes indeed are reflected on the db.