6

I'm trying to write a test that validates HTML returned from a generic class-based view. Let's say I have this function-based view that simply renders a template:

# views.py
from django.shortcuts import render

def simple_view(request, template='template.html'):
    return render(request, template)

With that, during testing I can just do:

# tests.py
from django.http import HttpRequest
from .views import simple_view

request = HttpRequest()
response = simple_view(request)

and then do the validation on the response. Now I would like to convert the above to a class-based view inheriting from TemplateView:

# views.py
from django.views.generic import TemplateView

class SimpleView(TemplateView):
    template_name = 'template.html'

Now essentially the same testing method fails:

# tests.py
from django.http import HttpRequest
from .views import SimpleView

request = HttpRequest()
view_func = SimpleView.as_view()
response = view_func(request).render()

results in

Traceback (most recent call last):
    File "tests.py", line 30, in test_home_page_returns_correct_html
response = view_func(request).render()
    File "lib/python2.7/site-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
    File "lib/python2.7/site-packages/django/views/generic/base.py", line 82, in dispatch
if request.method.lower() in self.http_method_names:
AttributeError: 'NoneType' object has no attribute 'lower'

I've tried setting request.method manually to GET, but that just brings up another error complaining about session not being in request.

Is there a way of getting a response from TemplateView with "empty" request?

Maciej Gryka
  • 8,181
  • 4
  • 34
  • 30
  • http://stackoverflow.com/questions/8603035/how-tdd-can-be-applied-to-django-class-based-generic-views – dm03514 Apr 21 '13 at 16:13

2 Answers2

6

Thanks dm03513 for the pointers! Indeed I had to use RequestFactory, but also make sure that request contains an empty session (despite SessionMiddleware being listed first):

# tests.py
from django.test import TestCase
from django.test.client import RequestFactory

class SimpleTest(TestCase):
    def setUp(self):
        self.factory = RequestFactory()

    def test_home_page_returns_correct_html(self):
        request = self.factory.get('/')
        request.session = {}
        view_func = SimpleView.as_view()
        response = view_func(request)
        response.render()
        self.assertIn('<html>', response.content)
Community
  • 1
  • 1
Maciej Gryka
  • 8,181
  • 4
  • 34
  • 30
  • 1
    request.session = {} failed in my testing. I set the session on the request as follows and it worked (import the modules first as needed of course): request.session = django.utils.importlib.import_module(django.conf.settings.SESSION_ENGINE).SessionStore(None) [I combined your answer with the answer in this question.](http://stackoverflow.com/questions/16865947/django-httprequest-object-has-no-attribute-session) – dmmfll Jun 20 '14 at 12:34
2

You could use djangos built in test client to accomplish this, instead of instantiating and calling view directly

test client allows you to make a request through django url router

response = self.get('/url/to/invoke/simpleview/')

additionally there are a couple blogposts I found touching on how to test class based views, one of which is http://tech.novapost.fr/django-unit-test-your-views-en.html

dm03514
  • 54,664
  • 18
  • 108
  • 145
  • True, though as pointed out here: http://stackoverflow.com/questions/8603035/how-tdd-can-be-applied-to-django-class-based-generic-views that's more functional than unit test (you're simulating a browser as opposed to just playing with functions). I guess in this case doesn't really matter, but it's good to know both are possible! – Maciej Gryka Apr 21 '13 at 16:54
  • On a second thought, using a client in this case is probably saner, if not the only, way! – Maciej Gryka Apr 22 '13 at 10:39