I have a class based view that works as designed in the browser. I'm trying to write unit tests for the view and they keep failing. I'm wondering why. The view (the UserPassesTest is whether the user is a superuser or not):
class EditUserView(LoginRequiredMixin, UserPassesTestMixin, TemplateView):
"""handles get and post for adding a new AEUser"""
template_name = 'editUser.html'
title = 'Edit User'
def get(self, request, *args, **kwargs):
"""handles the GET"""
post_url = reverse('edit_user', args=[kwargs['user_id']])
usr = get_object_or_404(AEUser, pk=kwargs['user_id'])
form = EditUserForm(initial={'is_active':usr.is_active, 'is_superuser':usr.is_superuser}, \
user=usr, request=request)
return render(request, self.template_name, \
{'title_text':self.title, 'post_url':post_url, 'form':form})
The Test Case:
class TestEditUser(TestCase):
"""test the AddUser view"""
#classmethod
def setUpTestData(cls):
cls.user = AEUser.objects.create_user(username='shawn', email='shawn#gmail.com', password='test')
cls.user.is_superuser = True
cls.user.save()
def setUp(self):
self.client = Client()
def test_get(self):
"""tests the GET"""
self.client.login(username=self.user.username, password=self.user.password)
get_url = reverse('edit_user', args=[self.user.id])
response = self.client.get(get_url, follow=True)
self.assertEqual(self.user.is_superuser, True)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'editUser.html')
I have 3 asserts in the test case. If I comment out the last two, and only assert that the user is a superuser, the test passes. For whatever reason, though, on the other two asserts, I get failures. The error I receive is:
AssertionError: False is not true : Template 'editUser.html' was not a template used to render the response. Actual template(s) used: 404.html, base.html, which leads me to believe the get_object_or_404 call is what's triggering the failure. Where am I going wrong with this test case? Thanks!
Test should be:
class TestEditUser(TestCase):
"""test the AddUser view"""
#classmethod
def setUpTestData(cls):
cls.user = AEUser.objects.create_user(username='shawn', email='shawn#gmail.com', password='test')
cls.user.is_superuser = True
cls.user.save()
def setUp(self):
self.client = Client()
def test_get(self):
"""tests the GET"""
self.client.login(username=self.user.username, password='test')
get_url = reverse('edit_user', args=[self.user.id])
response = self.client.get(get_url, follow=True)
self.assertEqual(self.user.is_superuser, True)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'editUser.html')
Related
I'm trying to test a view of my project with the following TestCase:
def test_jump_story(self):
c = APIClient()
user = User.objects.get(username='test1')
c.login(username=user.username, password='123')
room_id = PokerRoom.objects.get(name='planning').id
room_index = PokerRoom.objects.get(name='planning').index
request = c.post(reverse('jumpstory', kwargs={'pk': room_id, 'index': room_index}))
c.force_authenticate(user=user)
self.assertEqual(200,request.status_code)
but it returns this <Response status_code=401, "application/json"> even using force_authenticate.
The view that i'm testing:
class jumpStory(APIView):
permission_classes = [IsAuthenticated]
def post(self, request, pk, index):
data= self.request.data
index = self.kwargs['index']
pk = self.kwargs['pk']
if PokerRoom.objects.filter(id=pk).exists():
body = {'index':index}
message_socket("JUMP_STORY", pk, body)
return Response({'success':"JUMP_STORY"}, status=200)
else:
return Response({'error':'message not sended'}, status=400)
What is wrong with my test?
Use APITestCase instead of django TestCase. from rest_framework.test import APITestCase.
from rest_framework.test import APITestCase
class MyTests(APITestCase)
def setUp(self):
user = User.objects.create(username=john)
user.set_password("1234")
user.save()
self.client.force_authenticate(user=user)
def test_jump_story(self):
# do your test
I am trying to create test class for my custom middleware. The project is using Django REST framework. Middleware class works fine when server is running, but when I run test it behaves not quite as I would expect it to do. Maybe I misunderstood something, as I am quite new to testing in Django.
my_middleware.py:
class FX:
a = False
b = None
c = ''
def __init__(self) -> None:
pass
def __str__(self):
return 'fx ok'
class MyMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.fx = FX()
response = self.get_response(request)
print('done')
return response
views.py:
class TestView(APIView):
def get(self, request, format=None):
print('View ok')
print('FX: ', request.fx)
return Response({'result':'ok'})
tests.py:
class TestMyMiddleware(APITestCase):
#classmethod
def setUpTestData(cls):
pass
def setUp(self):
pass
def test_fx(self):
response = self.client.get(reverse('TestView'), content_type="application/json")
request = response.request
self.assertTrue(hasattr(request, 'fx'))
The code above actually runs the middleware. It prints "done" form the middleware call, then prints 'View ok' and also prints FX instance. However request.fx is not available in the test_fx method, thus giving assertion failure:
self.assertTrue(hasattr(request, 'fx'))
AssertionError: False is not true
Any idea what I might be doing wrong?
You need to access the request object from the response with response.wsgi_request instead of response.request.
class TestMyMiddleware(APITestCase):
#classmethod
def setUpTestData(cls):
pass
def setUp(self):
pass
def test_fx(self):
response = self.client.get(reverse('TestView'), content_type="application/json")
request = response.wsgi_request
self.assertTrue(hasattr(request, 'fx'))
I'm new to testing in DRF. I have the following ViewSet where I'm only allowing the 'List' and 'Retrieve' actions:
class RecipeViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
viewsets.GenericViewSet):
permission_classes = [permissions.AllowAny]
queryset = Recipe.objects.filter(visible=True).order_by('-created')
pagination_class = RecipeListPagination
lookup_url_kwarg = 'recipe_id'
def get_serializer_class(self):
if self.action == 'list':
return RecipeListSerializer
elif self.action == 'retrieve':
return RecipeDetailSerializer
These are the tests I've written so far:
class RecipeViewSetTestCase(test.APITestCase):
def setUp(self):
# Create objects
self.category = RecipeCategory.objects.create(name="meals")
Recipe.objects.create(name="Pepperoni Pizza", category=self.category, description="It's rounded")
Recipe.objects.create(name="Beef Ragu", category=self.category, description="It's moo'")
# Get urls
self.recipe = Recipe.objects.first()
self.list_url = reverse('recipes-list')
self.detail_url = reverse('recipes-detail', kwargs={'recipe_id': self.recipe.id})
def test_recipe_list(self):
response = self.client.get(self.list_url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data['results']), 2)
def test_recipe_detail(self):
response = self.client.get(self.detail_url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['name'], self.recipe.name)
def test_recipe_create_not_allowed(self):
response = self.client.post(self.list_url)
self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)
def test_recipe_delete_not_allowed(self):
response = self.client.delete(self.detail_url)
self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)
def test_recipe_update_not_allowed(self):
response = self.client.put(self.detail_url)
self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)
def test_recipe_partial_update_not_allowed(self):
response = self.client.patch(self.detail_url)
self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)
I'm not sure if it's necessary to write a test for each method that shouldn't be allowed, or if there is a better way.
Also, I'm not sure of what else I should be testing inside this viewset.
Does anyone with experience in testing DRF knows how to properly test viewsets?
Many thanks!
I am creating a django app which i which implement most of my logic before the design aspect of the application.
I created a testcase for user registration but anytime I post to the registration url, it received my posted data but never populates it to database.
class AuthTest(TestCase):
def setUp(self):
self.client = Client()
def test_reg_index(self):
response = self.client.get(reverse_lazy('register'))
return self.assertEqual(response.status_code, 200)
def test_registration(self):
data = {
'last_name': 'test_user',
'first_name': 'test_user',
'email': 'fashtop3#gmail.com',
'phone': '08035112897',
}
response = self.client.post(reverse_lazy('register'), data)
self.assertEqual(response.status_code, 302)
def test_login(self):
# self.client.login(username='foo', password='bar') # request.user is now user foo
data = {
'email': 'test_user#example.com',
'password': 'pass123',
}
response = self.client.post(reverse_lazy('login'), data)
self.assertEqual(response.status_code, 200)
class RegisterView(FormView):
"""
this handles users registration
"""
form_class = RegForm
template_name = "club/register.html"
success_url = reverse_lazy("register")
# def get(self, request, *args, **kwargs):
# return super(RegisterView, self).get(request, *args, **kwargs)
def form_invalid(self, form):
print(form.errors.as_data)
return super().form_invalid(form)
def form_valid(self, form):
"""
Process valid registration form
:param form:
:return: redirects to success_url
"""
random_password = ''.join(secrets.choice(string.ascii_uppercase + string.digits) for _ in range(6))
try:
with transaction.atomic():
"""Allow database transactions"""
data = form.cleaned_data
user = User.objects.create_user(
email=data['email'],
password=random_password,
remember_token=hashlib.sha1(str(datetime.now()).encode('utf-8')).hexdigest()
)
user.profile.last_name = data['last_name']
user.profile.first_name = data['first_name']
user.profile.phone = data['phone']
user.save()
send_notification.delay(user.pk, random_password)
print('Reg success')
messages.success(self.request, "Regular successfully registered "
"please check your mail for verification",
extra_tags="alert-success")
except IntegrityError as e:
print(e)
messages.error(self.request, "Registration Failed: Email address already exist", extra_tags="alert-danger")
return super(RegisterView, self).form_invalid(form)
except Exception as e:
print(str(e))
messages.error(self.request, "Registration Error, contact site administrator: ", extra_tags="alert-danger")
return super(RegisterView, self).form_invalid(form)
return super(RegisterView, self).form_valid(form)
The TestCase class wraps each test in a transaction, which is rolled back after the transaction (see the relevant documentation).
Even if test_registration successfully registers a user, it won't be available in test_login.
I would suggest creating a user for testing in a setUpClass method.
After like two days I got a solution to my Qts..
In order to retain your data after a success unit test you have to subclass a TestCase from unittest otherwise to perform auto rollback then import TestCase from django.test.
In order to do this, you need to use import unittest instead of from django.test import TestCase
And in your unit test class extends TestCase with unittest.TestCase
see the example below
import unittest
class MyTest(unittest.TestCase):
.......
Here's what I'm trying to do.
Use a mixin to validate ownership of an object.
Test the detail url to make sure that request.user == obj.owner
I expect the detail test to pass with a 200 assertion. But it's giving me a 302. But when I do a print from the mixing the request.user and owner are the same.
Here's my mixin:
from django.contrib.auth.mixins import UserPassesTestMixin
class IsOwnerMixin(UserPassesTestMixin):
"""
a custom mixin that checks to see if the user is the owner of the object
"""
def test_func(self):
# get the object
obj = self.get_object()
# if the obj.user == the logged in user they can see it otherwise boo!
if self.request.user == obj.owner:
return True
else:
return False
Here's my View:
class AwesomeDetail(LoginRequiredMixin, IsOwnerMixin, DetailView):
"""
An awesome detail
"""
model = models.Awesome
template_name = "awesomeness/detail.html"
Here's my Test:
from django.test import TestCase, RequestFactory
from django.test.client import Client
from django.contrib.auth.models import AnonymousUser, User, Group
from project.awesomness import views, models
class UrlsViewsTest(TestCase):
def setUp(self):
self.factory = RequestFactory()
self.user = User.objects.create_user(id='1', username='name', email='email#email.com', password='top_secret')
self.awesome = models.Awesome.objects.create(id='2', owner=self.user)
self.not_owner = User.objects.create_user(id='3', username='trouble', email='otheremail#otheremail.com', password='top_secret')
def test_awesome_detail(self):
"""
Test the awesome detail URL
"""
request = self.factory.get('/awesome/2/')
request.user = self.user
response = views.AwesomeDetail.as_view()(request, pk=2)
self.assertEqual(response.status_code, 200)
def test_awesome_not_owner(self):
"""
Test the awesome detail with a user that is not the owner
"""
request = self.factory.get('/awesome/2/')
request.user = self.not_owner
response = views.AwesomeDetail.as_view()(request, pk=2)
self.assertEqual(response.status_code, 302)
def test_awesome_detail_anonymous_user(self):
"""
Test the awesome detail with a user that is anonymous
"""
request = self.factory.get('/awesome/2/')
request.user = AnonymousUser()
response = views.AwesomeDetail.as_view()(request, pk=2)
self.assertEqual(response.status_code, 302)
And finally, here's the result:
Creating test database for alias 'default'...
F..
======================================================================
FAIL: test_awesome_detail (project.awesomeness.tests.UrlsViewsTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/dave/sandbox/project/project/awesomeness/tests.py", line 25, in test_awesome_detail
self.assertEqual(response.status_code, 200)
AssertionError: 302 != 200
----------------------------------------------------------------------
Ran 3 tests in 0.202s
FAILED (failures=1)
Destroying test database for alias 'default'...