Django testing tip: don’t test template output

When writing tests for [Django views](http://docs.djangoproject.com/en/dev/#the-view-layer), especially for projects at [work](http://www.ajc.com), I’ve almost completely abandoned any sort of detailed test for the template being rendered.

My tests usually look something like this:

def test_link_archive_should_show_published_links(self):
    """Links in draft status shouldn't appear in the archive."""
    #create some data for testing, optionally use a fixture
    l = Link(url="http://www.heisel.org/", title="Worst. Blog. Ever.", published=False)
    l.save()

    r = self.client.get("/links/archive/")
    self.assertValidResponse(r)
    self.assertContains(r, l.title, 0)
    
    l.published = True
    l.save()

    r = self.client.get("/links/archive/")
    self.assertValidResponse(r)
    self.assertContains(r, l.title, 1)

It’s slightly heretical, I know. But you don’t want your test suite breaking because of a change in HTML, or a change in your [date format](http://docs.djangoproject.com/en/dev/ref/templates/builtins/#now), or the addition of new output, etc.

Those changes are frequently made far down the road from the initial launch (say during a [redesign](http://blogs.ajc.com/ajc/2009/01/13/weve-been-making-some-changes/)) when you’re likely not to remember why you tested for such fine grained output.

Since I’m generally [testing first](http://www.xprogramming.com/xpmag/testFirstGuidelines.htm) (and who isn’t?) I use the view test as a chance to figure out what information — no matter the whims of the designer, destiny or the sands of time — really has to be presented to the user for this view to fulfill it’s job.

Test for the presence of those strings and move on.

This entry was posted in Django, Programming. Bookmark the permalink.

3 Responses to Django testing tip: don’t test template output

  1. I usually test for context. Which is then testing the backend code instead of the templates actually rendering the code. Seems like a good middle ground.

    • Chris says:

      Eric,

      I’m of two minds on testing the context.

      If you work with designers who change and deploy templates at will, then the context is almost a contract between the developer and the designer. It should be tested to ensure that you’re always providing the right data with the right template variable names.

      But if the developer is making the template changes then it feels like you’re actually testing an internal of the app. It doesn’t matter that the blog title is available as {{ blog.title }} in the template, it matters that the blog title appear when I GET the page.

  2. Karol says:

    I just found your blog post from two years ago and I feel tempted to add some from myself.

    I agree that very strict testing templates is a bad idea. However, when writing views for Android i tend to test the behavior of widgets or ‘views’ (call them whatever you like), and I like to do it for django templates too.

    In the example you present, you make a false assumption that some ‘drafts’ can be injected into the context and rendered. Well, thats not true, and shouldn’t be true unless your design is broken. It’s the views obligation to pass only valid data (no drafts) to the template. So it’s not the test that’s bad, but the design.

    There are some cases, when you must test templates. Simple example: pagination. Let’s say we’ve got three pages each on different url. When on first page there should be a link to ‘next’ pointing to second page. On second page we should have ‘previous’ and ‘next’ and only ‘previous’ on the third one. Presence of those links is essential. Otherwise the end user won’t be able to navigate, even if views work correctly. So we should test for presence of those links, shouldn’t we?

    It’s good to make tests as non-specific as possible. Just test the presence of the link anywhere in the template output.

    There may be some uber-clever way to deal with this problem in view-layer (controller), but nothing comes to my mind right now.

Comments are closed.