Crosstips.org

My fun Crossword solver project. Crosstips.org & Krysstips.se

Kung Fu

Fujian White Crane Kung Fu

Fry-IT

Fry-IT is the company I work for

Photos

Photoalbum, both old and new.

Zope

What I have and am doing with Zope

Receptsamlingen

In Swedish only. About my "Collection of Recipes" website.

Contact me

My contact details and how to contact me.

 

KungFuPeople.com
Do you train Kung Fu?
Or know someone who does?
Then check out KungFuPeople.com


Mobile version of this page Mobile version of this page


 

How to unit test the innards of a Django view function


14th of November 2008

Seconds ago I got this running and haven't yet fully understood what I've just done but the results are exactly what I need and this is going to be great.

Basically, in Django you have views like this:

 def _render(template, data, request):
    return render_to_response(template, data,
              context_instance=RequestContext(request))

 @login_required
 def club_page(request, year):
    variable1 = year / 4
    variable2 = variable1 * 2
    return _render("foo.html", locals(), request)

Now in my unit tests I don't want to have to call the view function and then have to dissect the resulting HTML just to figure out if the view function prepared the correct variables. So, here's my solution to this problem:

In 'test_views.py':

 from my_app.views import club_page

 # mutable globals for capturing the traffic through _render
 snatched_templates = []
 snatched_data = []
 snatched_requests = []
 def _fake_render(template, data, request):
    snatched_templates.append(template)
    snatched_data.append(data)
    snatched_requests.append(request)
    return render_to_response(template, data,
              context_instance=RequestContext(request))

 class FakeRequest(object):
    def __init__(self, get=None, post=None, user=None):
        self.get = get
        self.post = post
        self.user = user

    def GET(self):
        return self.get

    def POST(self):
        return self.post

 class ViewsTestCase(unittest.TestCase):
    def test_club_page(self):

         # add a test user
        from django.contrib.auth.models import User
        user = User.objects.create_user(username='t', password='t', 
                 email='t@t.com')

        # prepare a request 
        request = FakeRequest(user=staff)

        from my_app import views
        views._render = _fake_render

        response = club_day_page(request, 'fool', 2004)
        assert response.count('</html>')
        assert snatched_data[-1]['variable2'] == 102

This isn't really something a novice test driven Django developer can just copy since I've scribbled this code as pseudo code. My code for my app is a bit more complicated than this but the principle is the same.

So, tell me Django gurus, have I reinvented the wheel here or are other people finding this useful?

UPDATE

There is a more convenient way but it's quite arcane. After a client request has been made the response has an attribute context that you can use. E.g.:

 response = client.get('/club/Test_club2/attendance-report/')
 context_data = response.context[-1].dicts[0]
 pprint(context_data)

This approach, unlike my hack mentioned first, however requires that you have a publishable URL first.

Thanks Stephen!



Comment

James Bennett - 12th June 2009  [«« Reply to this]
The Django unit-testing documentation covers much easier ways to do this. For real-world examples, see the unit-test suite in the in-progress rewrite of my user-registration app:

http://bitbucket.org/ubernostrum/django-registration-backends/src/tip/registration/tests.py#cl-315
Peter Bengtsson - 23rd June 2009   [«« Reply to this]
404 on that.
James Bennett - 23rd June 2009  [«« Reply to this]
Yeah, I moved it last night into a module: http://bitbucket.org/ubernostrum/django-registration-backends/src/tip/registration/tests/views.py
 
Name:
Email:
hide my email address.

Your email address will be encoded to prevent email-extraction spiders from reading it so you won't get spammed if you decide to show your email address.