Django Middleware does not modify request in tests - django

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'))

Related

Testcase returning 401 even with force_autenthicate()

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

No "Authorization" header, how to access authorization header? Django

I need to check the Authorization HTTP Header of every incoming request.
First i have implemented Middleware. Now on website in devtools (when i post something) i see authorizational header with token.
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
user_id = request.POST.get('created_by', False)
try:
api_token = CustomUser.objects.get(user=user_id).api_token
except MyUser.DoesNotExist:
api_token = ''
response = self.get_response(request)
response['Authorization'] = "Bearer " + api_token
return response
class MyApiView(mixins.ListModelMixin, viewsets.GenericViewSet):
queryset = Event.objects.all()
serializer_class = EventSerializer
#action(methods=['POST'], detail=False)
def post(self, request):
print(request.META['HTTP_AUTHORIZATION']) **#keyerror**
print(request.META['Authorization']) **#keyerror**
print(request.headers.items()) **#no authorization header**
tutorial_serializer = MyApiSerializer(data=request.data)
if tutorial_serializer.is_valid():
tutorial_serializer.save()
return Response(tut`enter code here`orial_serializer.data, status=status.HTTP_201_CREATED)
return Response(tutorial_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
You're assigning header to wrong entity. Instead of adding header to response (what Django will return back to client), you need to add it to request headers:
from django.utils.deprecation import MiddlewareMixin
class CustomHeaderMiddleware(MiddlewareMixin):
def process_request(self, request):
user_id = request.POST.get('created_by', False)
try:
api_token = CustomUser.objects.get(user=user_id).api_token
except CustomUser.DoesNotExist:
api_token = ''
request.META['HTTP_Authorization'] = "Bearer " + api_token
response = self.get_response(request)
return response

Why is my Django test failing when the view actually works?

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')

Posting data using Django unit testing not saving to backend

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):
.......

Django 1.10: "new style" middleware equivalent of `process_request()`

How would one create "new style" middleware, which fulfills an equivalent implementation to using the process_request() hook with the "old style"?
I've already adapted pre 1.10 middleware process_request() using MiddlewareMixin...
from django.utils.deprecation import MiddlewareMixin
class MyCustomMiddleware(MiddlewareMixin):
def process_request(self, request):
# My request logic
return response
I'd like to know how to do a "pure" >1.9 "new style" implementation. I tried doing so by implementing __init__() and __call__() like this without luck:
class MyCustomMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# My request logic
return response
Thanks.
Here an example...
class TimeStampMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
response = self.get_response(request)
return response
Now you can get the timestamp in every request from yours views! (is only an example)