Seleniumless Django applications: using the test client

Some people waste a lot of time testing Django applications only with Selenium. No, I don’t think that you should not use Selenium to test Django applications, I just wanna show that nobody needs to use Selenium for all webtests in Django.

Selenium drives a browser, so you can have true test results on navigation, but Selenium is not needed to test a page title or a response status code, for example. Selenium tests are powerful, but slow, so you can preserve them to use when you really need, and use more simple and fast tools to more simple and fast tests.

Django has a powerful tool for web tests: the test client. Using this module, you can make requests and test the response (its content, status code, URL and everything inside a response).

Imagine that you have a Django application that manage all books of the world, and you want to test if the title of the /books page is “All the books of the world”, you can use Selenium, but IMHO, you should not use Selenium.

Suppose that there is the following Lettuce feature file:

Feature: List books
    In order to know which books are in the system
    I want to see the list of books

    Scenario: Listing all books in the world
        When I go to the /books URL
        Then the page title should be "All the books of the world"

If we use Selenium to define the scenario “Listing all books in the world”, we should have a terrain.py file with the following content:

from lettuce import before, after, world
from selenium import get_driver, FIREFOX

@before.all
def setup_browser():
    world.browser = get_driver(FIREFOX)

@after.all
def teardown_browser(total):
    world.browser.quit()

And define the steps definition with the following code:

# -*- coding: utf-8 -*-
from lettuce import step, world
from lettuce.django import django_url
from nose.tools import assert_equals

@step(u'When I go to the /books URL')
def when_i_go_to_the_books_url(step):
    world.browser.get(django_url('books'))

@step(u'Then the page title should be "(.*)"')
def then_the_page_title_should_be_group1(step, page_title):
    assert_equals(world.browser.get_title(), page_title)

There is nothing difficult here, the steps definition code is very simple and the test itself too, but how many time Lettuce spends running this test? Let’s check:

$ time ./manage.py harvest
Django's builtin server is running at 0.0.0.0:8000
Setting up a test database...OK

...

1 feature (1 passed)
1 scenario (1 passed)
2 steps (2 passed)

real 0m7.172s
user 0m0.740s
sys 0m0.270s

About 7 seconds to run a really really really simple and small test. We can earn time testing Django applications using the Django test client. Let’s rewrite the test above using Django test client, we just need to change the terrain.py:

from lettuce import before, world
from django.test import client

@before.all
def setup_browser():
    world.browser = client.Client()

And the step definitions:

# -*- coding: utf-8 -*-
from lettuce import step, world
from nose.tools import assert_equals
from lxml import html

@step(u'When I go to the /books URL')
def when_i_go_to_the_books_url(step):
    world.response = world.browser.get('/books/')

@step(u'Then the page title should be "(.*)"')
def then_the_page_title_should_be_group1(step, page_title):
    dom = html.fromstring(world.response.content)
    title = dom.xpath('//head/title')
    title = title.pop()
    assert_equals(title.text, page_title)

The only notice here is that is we have a little much code to test the title of the page. How many time Lettuce spends testing it?

$ time ./manage.py harvest
Django's builtin server is running at 0.0.0.0:8000
Setting up a test database...OK

...

1 feature (1 passed)
1 scenario (1 passed)
2 steps (2 passed)

real 0m3.288s
user 0m0.700s
sys 0m0.130s

About 3 seconds :) We didn’t need to open Firefox for visit the page and get it’s title, we just use the Django test client that knows how to deal with Django requests, and lxml to deal with the response content. Think about how many seconds you can earn just stopping to open a browser for the simplest tests of the world =P

There is a lot of other stuffs that you can do using the Django Test Client, and I plan to write more about it, but now, just check the official docs and have fun.

P.S.: Another great tool for Django testing is Django sane testing.

Happy testing :)

5 thoughts on “Seleniumless Django applications: using the test client

  1. >Thanks for this great and informative post. I hadn't come across lettuce before which has sparked my interest in getting serious with BDD.

  2. >And what about when you want to test javascript interaction on a page? I don't think people typically use selenium to test for content, you could use the built-in client for that:

    response = self.client.get('/home')
    self.assertEquals(response.status_code, 200)
    self.assertTrue('<title>All the books in the world</title>', response.content)

  3. >@David

    Selenium is useful for test javascript across different browsers, and the best tool for do that. My post is about wrong use of Selenium to test the page content, title or response status code.

    I agree with you when you say that the content can be tested using the built-in client.

    I use Selenium to tests across browsers, when I wish to test the content, I just use the built-in test client, or Sane testing.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Spam protection by WP Captcha-Free

Proudly powered by WordPress
Theme: Esquire by Matthew Buchanan.