How do I test a middleware that requires sessions in Django properly? - django

I'm trying to figure out how to test middleware in django. The middleware I'm writing logs in a user under certain conditions (if a key sent in email is valid). So obviously I'm dependent on django.contrib.auth and django.contrib.sessions.
I'm running into problems testing the login portion. I'm making a request like this:
user = User.objects.create_user('user', 'user#example.org', 'password')
key = LoginKey.objects.create(user=user)
request = self.factory.get('/', data={'auth_key': key.hash}) # self.factory is a RequestFactory()
self.middleware.process_request(request) # self.middleware is MyMiddleware()
That fails due to the session not being set. So next, I wrote a little snippet in my test class:
def make_session(self, request):
SessionMiddleware().process_request(request)
and that fails due to 'User' object has no attribute 'backend'. I'm not sure on the meaning of that, but I suspect I need to run all the middlewares I have installed.
I don't really want to make a fake view for this just to run a middleware, but I can't see another option at this point.
So I just wanted to know, before I have to chase this rabbit all the way down the hole, is there a way of doing this that doesn't require as much duct tape?

You should use the test client for this. That will ensure that the middleware is run and the session keys created.
response = self.client.get('/?auth_key=%s' % key.hash)
self.assertTrue(response.context['user'].is_authenticated()) # for example

Related

How to create a middleware that set a session variable on login in Django?

As the question says, I'd like to set some variable to user session when the user get logged and remove this variable on logout or browser close.
Initially I thought to write my custom login view to achieve this, but probably a middleware is a better solution.
The middleware wasn't the right way to achieve the solution. I discovered the signals and I implemented my solution in this way:
#receiver(user_logged_in)
def sig_user_logged_in(sender, user, request, **kwargs):
moderator = get_or_none(CompanyModerator, moderator__user=request.user)
if moderator:
if 'company_id' not in request.session:
request.session['company_id'] = moderator.company.id
The difference is that a middleware is triggered on each django request, when the needs was just do something when the user get logged-in/out. Each time that an user get logged-in/out we add some data/object-models etc.. to the session to track the user all around the website.
What do you think?
After going through all the comments to your question, I would also say the same as #akhil viswam in his last comment.
But now that you specified that you do want to do some custom tasks, then you can write your own custom middleware like this -> https://github.com/ankushrgv/notification/blob/master/apps/accounts/middlewares.py
and then include it in your settings.py like this -> https://github.com/ankushrgv/notification/blob/master/config/settings.py
Basically I was writing the user_id : session_id key value pairs in Redis and then reading them in my views to show real time notifications but only to the logged-in users.
I hope this will give you a fair idea about how to write your own custom middleware.

Django test RequestFactory vs Client

I am trying to decide whether I should use Django's Client or RequestFactory to test my views.
I am creating my server using DjangoRESTFramework and it's really simple, so far:
class SimpleModelList(generics.ListCreateAPIView):
"""
Retrieve list of all route_areas or create a new one.
"""
queryset = SimpleModel.objects.all()
serializer_class = SimpleModelSerializer
filter_backends = (IsOwnerFilterBackend,)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
What are the differences between testing with Django's Client and RequestFactory and which approach is more suited for testing a REST server (if there is any difference besides liking one better)?
Should I create tests with both so as to provide a better coverage for my system?
RequestFactory and Client have some very different use-cases. To put it in a single sentence: RequestFactory returns a request, while Client returns a response.
The RequestFactory does what it says - it's a factory to create request objects. Nothing more, nothing less.
The Client is used to fake a complete request-response cycle. It will create a request object, which it then passes through a WSGI handler. This handler resolves the url, calls the appropriate middleware, and runs the view. It then returns the response object. It has the added benefit that it gathers a lot of extra data on the response object that is extremely useful for testing.
The RequestFactory doesn't actually touch any of your code, but the request object can be used to test parts of your code that require a valid request. The Client runs your views, so in order to test your views, you need to use the Client and inspect the response. Be sure to check out the documentation on the Client.
When using Django REST framework request factory would be helpfull to test the permissions.
EX:
Class TestPermission(TestCase):
def test_admin_permisiion(self):
admin_user = User.objects.create(email='admin#gmail.com',password='admin997',is_staff=True)
factory = RequestFactory()
request = factory.get('/')
request.user = admin_user
permission = IsAdminUser()
has_permission = permission.has_permission(request, None)
self.assertTrue(has_permission)
what we have done hear is we created a admin user by setting is_staff=True , then we created a request and assigned the admin as user of the request. request factory helps us do so. then we checked the IsAdminUser() permission from DRF against the request. the test will pass .
Client is to be used when you need to test the response returned by an Api.

Why Django 1.4 per-site cache does not work correctly with CACHE_MIDDLEWARE_ANONYMOUS_ONLY?

I am working on a Django 1.4 project and writing one simple application using per-site cache as described here:
https://docs.djangoproject.com/en/dev/topics/cache/#the-per-site-cache
I have correctly setup a local Memcached server and confirmed the pages are being cached.
Then I set CACHE_MIDDLEWARE_ANONYMOUS_ONLY = True because I don't want to cache pages for authenticated users.
I'm testing with a simple view that returns a template with render_to_response and RequestContext to be able to access user information from the template and the caching works well so far, meaning it caches pages just for anonymous users.
And here's my problem. I created another view using a different template that doesn't access user information and noticed that the page was being cached even if the user was authenticated. After testing many things I found that authenticated users were getting a cached page if the template didn't print something from the user context variable. It's very simple to test: print the user on the template and the page won't be cached for an authenticated user, remove the user on the template, refresh the page while authenticated and check the HTTP headers and you will notice you're getting a cached page. You should clear the cache between changes to see the problem more clearly.
I tested a little more and found that I could get rid of the user in the template and print request.user right on the view (which prints to the development server console) and that also fixed the problem of showing a cached page to an authenticated user but that's an ugly hack.
A similar problem was reported here but never got an answer:
https://groups.google.com/d/topic/django-users/FyWmz9csy5g/discussion
I can probably write a conditional decorator to check if user.is_authenticated() and based on that use #never_cache on my view but it seems like that defeats the purpose of using per-site cache, doesn't it?
"""
A decorator to bypass per-site cache if the user is authenticated. Based on django.views.decorators.cache.never_cache.
See: http://stackoverflow.com/questions/12060036/why-django-1-4-per-site-cache-does-not-work-correctly-with-cache-middleware-anon
"""
from django.utils.decorators import available_attrs
from django.utils.cache import add_never_cache_headers
from functools import wraps
def conditional_cache(view_func):
"""
Checks the user and if it's authenticated pass it through never_cache.
This version uses functools.wraps for the wrapper function.
"""
#wraps(view_func, assigned=available_attrs(view_func))
def _wrapped_view_func(request, *args, **kwargs):
response = view_func(request, *args, **kwargs)
if request.user.is_authenticated():
add_never_cache_headers(response)
return response
return _wrapped_view_func
Any suggestions to avoid the need of an extra decorator will be appreciated.
Thanks!
Ok, I just confirmed my "problem" was caused by Django lazy loading the User object.
To confirm it, I just added something like this to my view:
test_var = "some text" + request.user
And I got an error message telling me I couldn't concatenate an str to a SimpleLazyObject. At this point the lazy loading logic hasn't got a real User object yet.
To bypass the lazy loading, hence return a non-cache view for authenticated users, I just needed to access some method or attribute to triggers an actual query on the User object. I ended up with this, which I think it's the simplest way:
bypass_lazyload = request.user.is_authenticated()
My conditional_cache decorator is no longer needed, although it was an interesting exercise.
I may not need to do this when I finish working with my views as I'll access some user methods and attributes on my templates anyway but it's good to know what was going on.
Regards.

django - get user logged into test client

In a written django test, how can I get the current logged in user?
For example this is the test I want to write:
def test_author_set_once(self):
self.client.login(username='Adam', password='password')
#create an object and test the author is adam
self.client.login(username='Barry', password='password')
#modify the first object and test that the author has not changed
So I want to be able to say something like...
self.assertEqual(object.author, self.client.user)
(but I can't)
The way I've coded it at the moment is like this:
self.client.login(username='Adam', password='password')
self.user = User.objects.get(username='Adam')
#create an object
self.assertEqual(object.author, self.user)
This relies on the assumption that the request.user is the same as a particular user object. I guess it's OK but it seems a bit clunky.
Is there anyway to avoid the assumption?
The test client is request-agnostic. It doesn't inherently hold information about what users are logged in. (Neither does your actual webserver or the Django dev server, either, for obvious reasons, and those same reasons apply here).
login is simply a convenience method on the test client to essentially mimic a POST to /login/ with the defined user credentials. Nothing more.
The actual user is available on the request just like in a view. However, since you don't have direct access to the view, Django makes request available on the view's response. After you actually use the test client to load a view, you can store the result and then get the user via:
response.request.user
More recent versions of Django will use:
response.wsgi_request.user
Actually you can't access the current user via a test client response.
However, you can check if some user is logged in. Inspecting self.client.session will do:
self.client.session['_auth_user_id']
>>> 1
There is a more detailed answer for this.
I don't know which version you are using. Since 1.10.2, there is a wsgi_request attribute in response, it serves as request object in your view.
So It's very simple to get logged in user:
response.wsgi_request.user
You can log in with a test user like so:
from django.contrib.auth.models import User
user = User.objects.create_user('foo', 'myemail#test.com', 'bar')
self.client.login(username='foo', password='bar')
Now, you can just use user in your tests:
self.assertEqual(created_model_object.user, user)

django test client gets 404 for all urls

I am doing my first experiments with django testing and I am having the problem that I always get the 404 template regardless which url (even /) I am using.
If I throw the very same code into the django shell it's working as expected and always presents me the contents of the requested url.
class SimpleTest(TestCase):
def setUp(self):
self.user = User.objects.create_user('test', 'test', 'test')
self.user.is_staff = True
self.user.save()
self.client = Client()
def test_something(self):
self.assertTrue(self.client.login(username='test', password= 'test'))
self.client.get("/")
The login returns True, but the get() fails. Any hints what I am doing wrong here?
Keep in mind that most views use something like get_object_or_404, get_list_or_404, or simply raise Http404 when there's a problem accessing some object or another. You'll need to make sure that your test database is populated with sufficient objects to fulfill all these requirements to make the view not return a 404.
Remember, when running tests, the database is rolled back after each test (using transactions), so each test method must stand on its own or the setUp method must populate the database with any required dependencies.