Unit-Testing Django Views

112 views
Skip to first unread message

OnlineJudge95

unread,
Mar 28, 2019, 12:16:43 PM3/28/19
to Django users
Hi people,

I am following the book Test-Driven Development with Python by Harry J.W. Perceval. The book is using Django version 1.7 which is outdated as of now so I started with version 2.1.

I am trying to unit test my index view. One unit-test that I have written is testing whether the index view returns correct HTML or not by comparing the input received through
django.template.loader.render_to_string
the unit-test fail with the following traceback
python manage.py test
Creating test database for alias 'default'...
.System check identified no issues (0 silenced).
F.
======================================================================
FAIL: test_index_view_returns_correct_html (lists.tests.IndexViewTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests.py", line 24, in test_index_view_returns_correct_html
    self.assertEqual(expected_html, actual_html)
AssertionError: '<!DO[263 chars]lue="BJMT1b9fxuXOGugp00SDypeTYZxvlmc6KtBSYMDon[198 chars]l>\n' != '<!DO[263 chars]lue="R05ZiWMASEWMurA8Rdo8bnA0mTwqFTqA0KUYfxgJI[198 chars]l>\n'

----------------------------------------------------------------------
Ran 3 tests in 0.006s

FAILED (failures=1)
Destroying test database for alias 'default'...

Process finished with exit code 1


It was clear that the csrf token is causing the test to fail. Is there any way to test it, or should it be tested? I ask this as when I changed my Django version to 1.7, the tests were passing, even after giving the csrf token field in the form. I tried going through the changelogs but 1.7 is far behind (beginner here). Please find the code snippets, directory structure provided below.


lists/views.py
from django.http import HttpResponse
from django.shortcuts import render


# Create your views here.
def index(request):
    if request.method == 'POST':
        return HttpResponse(request.POST['item_text'])
    return render(request, 'index.html')

lists/test.py
from django.http import HttpRequest
from django.template.loader import render_to_string
from django.test import TestCase
from django.urls import resolve

from lists.views import index


# Create your tests here.
class IndexViewTest(TestCase):

    def test_root_url_resolves_to_home_page_view(self):
       [...]

    def test_index_view_returns_correct_html(self):
        request = HttpRequest()
        response = index(request)
        actual_html = response.content.decode()
        expected_html = render_to_string('index.html', request=request)

        self.assertEqual(expected_html, actual_html)

    def test_index_view_can_save_a_post_request(self):
       [...]

requirements.txt
Django==2.1.7
pytz==2018.9
selenium==3.141.0
urllib3==1.24.

settings.py
[...]
# Application definition

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'lists',
]

[...]

Directory Structure

Screen Shot 2019-03-28 at 9.36.56 PM.png


Simon Charette

unread,
Mar 28, 2019, 12:54:09 PM3/28/19
to Django users
This is effectively failing because of a mechanism added in 1.10 to protect
against BREACH attacks[0] by salting the CSRF token.

I'm not aware of any way to disable this mechanism but testing against the
exact HTML returned from a view seems fragile.

I suggest you use assertContains[1] (with or without html=True) and
assertTemplateUsed[2] instead.

Cheers,
Simon

Aldian Fazrihady

unread,
Mar 28, 2019, 3:28:17 PM3/28/19
to django...@googlegroups.com
There are several things you can try:
1. Mocking csrf token functions
2. Passing the csrf token context from first HTML generation to the second HTML generation
3. Wiping out the csrf token parts from both HTML before comparing them.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/7f0f12be-fc3e-43c3-ba07-e48158def051%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Chetan Ganji

unread,
Mar 28, 2019, 4:28:20 PM3/28/19
to django...@googlegroups.com
I would prefer to just check the status code of the response object. 
This is what I have done. You have to check if it works for forms with csrf_token or not.


class TestReverseUrls(TestCase):

def setUp(self):
self.client = Client()
self.reverseUrls = ['index', 'login', 'register']
self.response = {url:self.client.get(reverse(url)) for url in self.reverseUrls}

def test_reverse_urls(self):
for url in self.reverseUrls:
self.assertEqual(self.response[url].status_code, 200)



Regards,
Chetan Ganji
+91-900-483-4183


Chetan Ganji

unread,
Mar 28, 2019, 4:54:31 PM3/28/19
to django...@googlegroups.com
There is one more way you could do it 


class TestIndexPageLoad(TestCase):

def setUp(self):
self.client = Client()
self.response = self.client.get('/')

def test_check_response(self):
self.assertTemplateUsed(self.response, 'index.html')



Regards,
Chetan Ganji
+91-900-483-4183

Test Bot

unread,
Mar 30, 2019, 7:16:51 AM3/30/19
to django...@googlegroups.com
Thanks everyone for the clearance of the problem. I will remove the unit test's logic to check fir template, it seems a viable test case along with the status code value.

Test Bot

unread,
Mar 30, 2019, 7:30:46 AM3/30/19
to django...@googlegroups.com
I tried removing the {% csrf_token %} from my index.html But it seemed to have no effect. I got the following traceback for further clarity

                                                                                                              START OF TRACEBACK
===================================================================================================================================
python superlists/manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.F.
Destroying test database for alias 'default'...
======================================================================
FAIL: test_index_view_returns_correct_html (lists.tests.HomePageTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "superlists/lists/tests.py", line 25, in test_index_view_returns_correct_html
    self.assertEqual(expected_html, actual_html)
AssertionError: '<!DO[267 chars]lue="UUl2QtIopzqg9Co4uPlTlSuEPP2O44aMhda056gd4[201 chars]l>\n' != '<!DO[267 chars]lue="46hpOIUSFV0Qm4Z4cwq9OORHGeLPcNlGrp6n3lsHk[201 chars]l>\n'

----------------------------------------------------------------------
Ran 3 tests in 0.005s

FAILED (failures=1)

Process finished with exit code 1
===================================================================================================================================
                                                                                                                   END OF TRACEBACK


Test Bot

unread,
Mar 30, 2019, 7:40:56 AM3/30/19
to django...@googlegroups.com
Sorry for the bother but I finally solved it by using Django's Test Client and checking for status_code and template used to render the response in my unit-test.

Regards,
Test Bot

Jorge Gimeno

unread,
Mar 30, 2019, 4:34:24 PM3/30/19
to django...@googlegroups.com
The online version of the book is pinned to Django 1.11. You may want to check that out.

-Jorge

Reply all
Reply to author
Forward
0 new messages