I have a decorator where I can verify if the user has any permission. The code is working for me but I want to write a test for it.
How can I test the any_permission_required function?
from django.contrib.auth.decorators import user_passes_test
def any_permission_required(*perms):
return user_passes_test(lambda u: any(u.has_perm(perm) for perm in perms))
#any_permission_required('app.ticket_admin', 'app.ticket_read')
def ticket_list(request):
...
Finally with help of Alasdair and the Django test code I found a solution.
from django.test import RequestFactory
class TestFoo(TestCase):
def setUp(self):
self.user = models.User.objects.create(username='foo', password='bar')
self.factory = RequestFactory()
def test_any_permissions_pass(self):
perms = Permission.objects.filter(codename__in=('ticket_admin', 'ticket_read'))
self.user.user_permissions.add(*perms)
#any_permission_required('app.ticket_admin', 'app.ticket_read')
def a_view(request):
return HttpResponse()
request = self.factory.get('/foo')
request.user = self.user
resp = a_view(request)
self.assertEqual(resp.status_code, 200)
Related
I would like to return a 401 message if the user is not enabled. When I try returning a response instead of a token it doesn't work which I understand to be because the serializer is expecting the token. How do I customise it to send a 401 response if the user is not enabled please?
My custom token class is as below:
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework import status
from rest_framework.response import Response
class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
#classmethod
def get_token(cls, user):
if user.is_enabled:
token = super().get_token(user)
# Add custom claims
token['name'] = user.name
token['gender'] = user.gender
return token
else:
return Response({'detail':'Account not enabled'}, status=status.HTTP_401_UNAUTHORIZED)
class CustomTokenObtainPairView(TokenObtainPairView):
serializer_class = CustomTokenObtainPairSerializer
The URL root looks like:
re_path(r'^authenticate/',CustomTokenObtainPairView.as_view(), name='authenticate'),
This is what works for me, it's a simple solution and gives me the result I need which is why I'm posting this as an answer.
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework import status
from rest_framework.response import Response
from rest_framework_simplejwt.exceptions import InvalidToken
class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
#classmethod
def get_token(cls, user):
if user.is_enabled:
token = super().get_token(user)
# Add custom claims
token['name'] = user.name
token['gender'] = user.gender
return token
else:
raise InvalidToken("User is not enabled.")
You can return some symbol like None in Python from get_token if the user is not enabled and then override the get method of CustomTokenObtainPairView to return 401 if the value of get_token is None.
EDIT: Found a better way, move the check of is_enabled to the post request from the serializer. Below is the code.
class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
#classmethod
def get_token(cls, user):
token = super().get_token(user)
# Add custom claims
token['name'] = user.name
token['gender'] = user.gender
return token
from rest_framework.permissions import IsAuthenticated
class CustomTokenObtainPairView(TokenObtainPairView):
permission_classes = [IsAuthenticated]
serializer_class = CustomTokenObtainPairSerializer
def post(self, request, *args, **kwargs):
is self.request.user.is_enabled:
return super().post(request, *args, **kwargs)
else:
return Response({'detail':'Account not enabled'}, status=status.HTTP_401_UNAUTHORIZED)
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 writing Unittests for the Login on Django. And I have serious problems with the get_user(). I just can't figure out how to get through this function in test. I can't make a mock, and I can't substitute values. I think I'm wrong do it. But I don't know how do it right!
Please, I need help!
def user_login(request):
if request.method == 'POST':
form = UserLoginForm(data=request.POST)
if form.is_valid():
user = form.get_user()
login(request, user)
return redirect('/')
I need unittest for this simply function.
Here is a very basic test for the login view, it asserts that a 200 response is returned when someone logs in. I don't think that mocks are required. For completeness you would most likely want to add tests for incorrect username/password and deactivated accounts
import unittest
from django.test import Client
from django.contrib.auth import get_user_model
class SimpleTest(unittest.TestCase):
def setUp(self):
self.client = Client()
self.username = 'foo'
self.password = 'bar'
self.user = get_user_model().objects.create_user(self.username, password=self.password)
def test_login(self):
response = c.post('/login/', {'username': self.username, 'password': self.password})
self.assertEqual(response.status_code, 200)
I have one ListView and one DetailView and both requires LoginRequiredMixin. Now i want to write unit test for these class based views. Can somebody help me with this. I want to test the template, context as well as right view is called. My Tests.py Looks like this:-
from django.test import TestCase, Client
from django.urls import reverse,resolve
from django.test.client import RequestFactory
from django.contrib.auth import get_user_model
from teams.models import Teams
from teams.views import TeamListView, TeamDetailView
class TestTeamsView(TestCase):
def setUp(self):
self.factory = RequestFactory()
def test_list_view(self):
request = self.factory.get(reverse('teams:team_list'))
response = TeamListView.as_view()(request)
self.assertEqual(response.status_code, 200)
print(response.status_code)
I am getting Response:
if not request.user.is_authenticated:
AttributeError: 'WSGIRequest' object has no attribute 'user'
I got the solution and its like this:
from django.test.client import RequestFactory
from django.contrib.auth.models import AnonymousUser
class TestTeamsView(TestCase):
def setUp(self):
self.factory = RequestFactory()
self.user = User.objects.create_user(
username='test1',
email='abc1#gmail.com',
first_name='t',
last_name='u',
password='password'
)
def test_team_list_view_with_valid_user(self):
request = self.factory.get('/team/')
request.user = self.user
response = TeamListView.as_view()(request)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.template_name[0], 'teams/teams_list.html')
def test_team_list_view_with_anonymous_user(self):
request = self.factory.get('/team/')
request.user = AnonymousUser()
response = TeamListView.as_view()(request)
self.assertEqual(response.status_code,302)
I am writing a test for a DetailView that queries get_object() by accessing a value set in a Middleware. This is for a Companies application and Company Model. Each user is in a Company.
To access the company throughout the project, I set the current user's Company.uuid on the request via a custom middleware.
Middleware
from django.utils.deprecation import MiddlewareMixin
class DynamicCompanyUUIDMiddleware(MiddlewareMixin):
""" Adds the current organization's UUID from the current user."""
def process_request(self, request):
try:
company_uuid = request.user.company_uuid
except:
company_uuid = None
request.company_uuid = company_uuid
That is used in the CompanyDetailView's get_object() method via a Mixin that I use for the other Company Views.
Mixin
class CompanyMixin(LoginRequiredMixin, SetHeadlineMixin):
model = Company
def get_object(self):
return get_object_or_404(
self.model,
uuid=self.request.user.company_uuid)
Test
The test that I'm trying to write is:
from django.test import RequestFactory
from django.urls import reverse, resolve
from test_plus.test import TestCase
from ..models import Company
from ..views import CompanyDetailView
class BaseCompanyTestCase(TestCase):
def setUp(self):
self.user = self.make_user()
self.object = Company.objects.create(owner=self.user, name="testcompany")
self.user.company_uuid = self.object.uuid
self.factory = RequestFactory()
class TestCompanyDetailView(BaseCompanyTestCase):
def setUp(self):
super(TestCompanyDetailView, self).setUp()
self.client.login(username="testuser", password="password")
self.view = CompanyDetailView()
self.view.object = self.object
request = self.factory.get(reverse('companies:detail'))
request.user = self.user
request.company_uuid = self.user.company_uuid
response = CompanyDetailView.as_view()(request)
self.assertEqual(response.status_code, 200)
def test_get_headline(self):
self.assertEqual(
self.view.get_headline(),
'%s Members' % self.object.name
Result
This results in a 404 with the testuser's company not being found.
Walking through it:
I create the user
Create the company for this new testuser
Set the user.company_uuid
This should allow the mixin to access the company_uuid
Therefore return the user's company in the request
However I'm not returning the company as the 404 shows.
Question
Where am I going wrong on this? Thanks in advance for your help.
Answer
I was mixing Django's Client & RequestFactory. I have corrected the code above which is correct.
I was mixing Django's Client & RequestFactory. After stepping away, I figured it out below -
from django.test import RequestFactory
from django.urls import reverse, resolve
from test_plus.test import TestCase
from ..models import Company
from ..views import CompanyDetailView
class BaseCompanyTestCase(TestCase):
def setUp(self):
self.user = self.make_user()
self.object = Company.objects.create(owner=self.user, name="testcompany")
self.user.company_uuid = self.object.uuid
self.factory = RequestFactory()
class TestCompanyDetailView(BaseCompanyTestCase):
def setUp(self):
super(TestCompanyDetailView, self).setUp()
self.client.login(username="testuser", password="password")
self.view = CompanyDetailView()
self.view.object = self.object
request = self.factory.get(reverse('companies:detail'))
request.user = self.user
request.company_uuid = self.user.company_uuid
response = CompanyDetailView.as_view()(request)
self.assertEqual(response.status_code, 200)
def test_get_headline(self):
self.assertEqual(
self.view.get_headline(),
'%s Members' % self.object.name