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:
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 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:
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:
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 django.test import client
@before.all
def setup_browser():
world.browser = client.Client()
And the step definitions:
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?
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 :)
>Thanks for this great and informative post. I hadn't come across lettuce before which has sparked my interest in getting serious with BDD.
>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)
>How about Pycurracy and Windmill? Have you done something with them?
>@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.
>@Heberth
I never heard about Windmill. I will check it.
Thanks ;)