Search code examples
pythondjangohttpclientdjango-testingdjango-unittest

How to test get request in Django with a data?


I am now writing tests for my web application in Django. I have an URL 127.0.0.1/home/device/(?P<item>[^/]+). I was testing an invalid URL path. Here item is a device name in the database. Consider an invalid device and while testing I have given the following code:

response=self.client.get("/health/errorfiles/",{'item':'testdevice_R'})

This give me a 404 response. The same thing I have tried with:

response=self.client.get("/health/errorfiles/testdevice_R/")

But this time, the test runner executes my view function and gives a TypeError since it is not an invalid device.

In both of the methods, which one is correct usage for a get request?

views.py

def showfiles(request,item):

if request.user.is_anonymous():                                                             ## check if the user is valid
    return HttpResponseRedirect("/")

db = MySQLdb.connect(host='localhost',user=root,passwd='123mcvis',db='test_db')

s = db.cursor()                                                                      
username=request.user                                                                     
s.execute("Select tzone,type from device where user='%s' and device='%s'"%(username,item)                     
tz=s.fetchone()
timezone.activate(tz[0])
s.close()
return render(request,"Home/showdevices.html")

This is my view function and since the device is invalid, it shows typ error.

class showfile_test(TestCase):
     def test_invalid(self):

           response=self.client.get("/health/errorfiles/testdevice_R/")
           self.assertEqual(response.status_code,404)

Traceback

   ERROR: test_invalid (HealthApp.tests2.showfile_test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/vishnumc/vishnu/project/django/official/version6.2.3/HealthApp/tests2.py", line 162, in test_showfiles_with_invalid_deviceid
    response=self.client.get("/health/errorfiles/invaliddevice/")
  File "/home/vishnumc/vishnu/project/env/local/lib/python2.7/site-packages/django/test/client.py", line 529, in get
    **extra)
  File "/home/vishnumc/vishnu/project/env/local/lib/python2.7/site-packages/django/test/client.py", line 333, in get
    return self.generic('GET', path, secure=secure, **r)
  File "/home/vishnumc/vishnu/project/env/local/lib/python2.7/site-packages/django/test/client.py", line 409, in generic
    return self.request(**r)
  File "/home/vishnumc/vishnu/project/env/local/lib/python2.7/site-packages/django/test/client.py", line 494, in request
    six.reraise(*exc_info)
  File "/home/vishnumc/vishnu/project/env/local/lib/python2.7/site-packages/django/core/handlers/exception.py", line 39, in inner
    response = get_response(request)
  File "/home/vishnumc/vishnu/project/env/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 249, in _legacy_get_response
    response = self._get_response(request)
  File "/home/vishnumc/vishnu/project/env/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/vishnumc/vishnu/project/env/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/vishnumc/vishnu/project/django/official/version6.2.3/HealthApp/views.py", line 118, in showfiles
    timezone.activate(tz[0])                                                                        ##setting current timezone to user's time zone for display timestamps
TypeError: 'NoneType' object has no attribute '__getitem__'

Solution

  • The first example is a GET request to /health/errorfiles/?item=testdevice_R. In the view, you would then find item in request.GET.

    The second example is a GET request to /health/errorfiles/testdevice_R/. There is no data in request.GET and Django will pass item to your view since you have a named group (?P<item>[^/]+) in your URL pattern.

    You should use the second version, because you want to test the URL pattern r'/home/device/(?P<item>[^/]+)'.

    The second version of your test has uncovered problems in your view. You need to fix the view so that it doesn't raise TypeError.

    Ideally, you shouldn't be writing raw SQL like that. Django allows you to do something like the following:

    from .models import Device
    
    from django.shortcuts import get_object_or_404, render
    
    def showhome(request, item):
        device = get_object_or_404(Device, user=request.user, item=item)
        if device is not None:
            timezone.activate(device.tzone)
        return render(request,"Home/showdevices.html", {'device': device})
    

    If you must use raw SQL then don't use string substitution %(username,item). Your current code exposes you to an SQL injection account. You should change it to:

    s.execute("Select tzone,type from device where user=%s" and device=%s, (username, item))
    

    Your code then has handle the case where tz is None, to avoid the TypeError.

    tz = s.fetchone()
    if tz is not None:
        timezone.activate(tz[0])
    else:
        # Decide what to do if no items returned