January 21, 2009

Django testing tip: don’t test template output

When writ­ing tests for Django views, espe­cially for projects at work, I’ve almost com­pletely aban­doned any sort of detailed test for the tem­plate being rendered.

My tests usu­ally look some­thing 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 hereti­cal, I know. But you don’t want your test suite break­ing because of a change in HTML, or a change in your date format, or the addi­tion of new output, etc.

Those changes are fre­quently made far down the road from the ini­tial launch (say during a redesign) when you’re likely not to remem­ber why you tested for such fine grained output.

Since I’m gen­er­ally test­ing first (and who isn’t?) I use the view test as a chance to figure out what infor­ma­tion — no matter the whims of the designer, des­tiny or the sands of time — really has to be pre­sented to the user for this view to ful­fill it’s job.

Test for the pres­ence of those strings and move on.

Filed under: Django,Programming

Next:
Previous:

Related

  • http://ericholscher.com Eric Holscher

    I usu­ally test for con­text. Which is then test­ing the back­end code instead of the tem­plates actu­ally ren­der­ing the code. Seems like a good middle ground.

  • http://heisel.org Chris

    Eric,

    I’m of two minds on test­ing the context.

    If you work with design­ers who change and deploy tem­plates at will, then the con­text is almost a con­tract between the devel­oper and the designer. It should be tested to ensure that you’re always pro­vid­ing the right data with the right tem­plate vari­able names.

    But if the devel­oper is making the tem­plate changes then it feels like you’re actu­ally test­ing an inter­nal of the app. It doesn’t matter that the blog title is avail­able as {{ blog.title }} in the tem­plate, it mat­ters that the blog title appear when I GET the page.

  • Karol

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

    I agree that very strict test­ing tem­plates is a bad idea. How­ever, when writ­ing views for Android i tend to test the behav­ior of wid­gets or ‘views’ (call them what­ever you like), and I like to do it for django tem­plates too.

    In the exam­ple you present, you make a false assump­tion that some ‘drafts’ can be injected into the con­text and ren­dered. Well, thats not true, and shouldn’t be true unless your design is broken. It’s the views oblig­a­tion to pass only valid data (no drafts) to the tem­plate. So it’s not the test that’s bad, but the design.

    There are some cases, when you must test tem­plates. Simple exam­ple: pag­i­na­tion. Let’s say we’ve got three pages each on dif­fer­ent url. When on first page there should be a link to ‘next’ point­ing to second page. On second page we should have ‘previous’ and ‘next’ and only ‘previous’ on the third one. Pres­ence of those links is essen­tial. Oth­er­wise the end user won’t be able to nav­i­gate, even if views work cor­rectly. So we should test for pres­ence of those links, shouldn’t we?

    It’s good to make tests as non-​specific as pos­si­ble. Just test the pres­ence of the link any­where in the tem­plate output.

    There may be some uber-​clever way to deal with this prob­lem in view-​layer (con­troller), but noth­ing comes to my mind right now.