2

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__'
Alasdair
  • 298,606
  • 55
  • 578
  • 516
vishnu m c
  • 841
  • 1
  • 7
  • 21
  • can we have more of your test file please? are you using the test classes from django? – Bestasttung Sep 21 '17 at 08:48
  • Yeah. I am using TestCase class – vishnu m c Sep 21 '17 at 08:49
  • @Alasdair when i request with not slashing end, the response is 301 – vishnu m c Sep 21 '17 at 09:01
  • Yeah i have added traceback. – vishnu m c Sep 21 '17 at 09:27
  • @Alasdair , For the sake of simplicity i have just renamed the files that why the difference found. Can you please look it now – vishnu m c Sep 21 '17 at 09:55
  • Your code is not handling the case where `tz` is `None`. – Alasdair Sep 21 '17 at 10:10
  • @Alasdair Yeah i have noticed this in view. Type error raised because of that invalid device. My doubt is about the get request simulation. Please understand my problem. – vishnu m c Sep 21 '17 at 10:16
  • As I said originally, the second version of your test `self.client.get("/health/errorfiles/testdevice_R/")` is ok. It has uncovered the fact that your view is not handling invalid data properly. You should fix this, ideally by switching to using the ORM, or simply by adding a check for `if tz is None`. – Alasdair Sep 21 '17 at 10:20
  • OK thanks for that. Actually my doubt is whether the code `response=self.client.get("/health/errorfiles/",{'item':'testdevice_R'})` is correct. Or how can i use get data with dictionary. – vishnu m c Sep 21 '17 at 10:22
  • If both request are same, then i have to get TypeError in both approach. But the first one givem me 404 response. This is what i am asking. – vishnu m c Sep 21 '17 at 10:24
  • No. The two requests are different. As I keep saying, you should use the second one. The first one is testing a completely different url `/health/errorfiles/`, and includes the item in the `GET` data rather than the URL. – Alasdair Sep 21 '17 at 10:34
  • Can you please give me the difference in `get data` and `data in url`. As of my knowledge both are same, if i am wrong please help me. – vishnu m c Sep 21 '17 at 10:37
  • 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 argument `(?P[^/]+)` in your URL pattern. – Alasdair Sep 21 '17 at 12:13
  • Yeah i got. Thanks @Alasdair. Can you please give it as answer, then i can accept the answer. Hence it will be clear to everyone – vishnu m c Sep 22 '17 at 05:23

1 Answers1

1

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
Alasdair
  • 298,606
  • 55
  • 578
  • 516
  • I am not using django ORM or django models. I am using MySQLdb to connect with database – vishnu m c Sep 21 '17 at 09:20
  • 1
    If you don't use the ORM you are ignoring one of the key advantages of Django. Your `execute()` code is insecure and susceptible to SQL injection, because you are using string substitution instead of escaping the parameters. If you use the ORM as above then you avoid that issue. – Alasdair Sep 21 '17 at 09:45
  • But django ORM is very slow compared to MYSQLdb – vishnu m c Sep 21 '17 at 09:56
  • There may be some cases where the Django ORM is slow, but for a simple fetch like this I'd be surprised if the difference is noticeable. – Alasdair Sep 21 '17 at 10:06
  • Did you miss a ' " ' in `s.execute("Select tzone,type from device where user=%s and device=%s, (username, item))` ? – vishnu m c Sep 21 '17 at 10:26
  • I've added the missing `"`. – Alasdair Sep 21 '17 at 10:33