Selenium tests with Django 1.4+

With Django 1.4 support for in-browser testing frameworks, it was time to revisit our previous blog posts dedicated to Selenium: Selenium, Python and Jenkins on Debian 1, 2 and 3.

Sample project

This tutorial is based on the poll application from the Django tutorial. It describes all the required steps to run Selenium tests on a Django 1.4+ project, keeping in mind that the final step is to use Jenkins as a continuous integration server. The source code can be found on the django14 branch of this repository.

This sample project is organized as follows:

image

The following features are implemented:

  • Poll management via the Django admin site,
  • Vote submission.

image

Testing tools

In this tutorial, the following tools are used:

  • django-jenkins: a Django test runner dedicated to Jenkins,
  • coverage (via django-jenkins): a tool for measuring code coverage of Python programs,
  • pylint (via django-jenkins): a static code analyzer looking for bugs and signs of poor code quality.

Installation

First clone the repository:

$ git clone -b django14 https://github.com/shiningpanda/djangotutorial-selenium.git

Step in the new djangotutorial-selenium folder, and install all requirements with pip by typing:

$ pip install -r requirements.txt

Configure tests

Edit djangotutorial/settings.py to enable django_jenkins in the INSTALLED_APPS, then define the applications you want to test with the PROJECT_APPS settings. In this example we want to test the poll application:

INSTALLED_APPS = (
    # Other applications...
    'django_jenkins',
)
# Other settings...
PROJECT_APPS = [
    'polls',
]

Write tests

Selenium tests are located in the polls/tests.py module.

All our tests inherit from the following django.test.LiveServerTestCase base class, which launch a live Django server in the background on setup, and shuts it down on teardown.

from django.test import LiveServerTestCase
from selenium.webdriver.firefox.webdriver import WebDriver

class BaseTestCase(LiveServerTestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = WebDriver()
        super(BaseTestCase, cls).setUpClass()

    @classmethod
    def tearDownClass(cls):
        super(BaseTestCase, cls).tearDownClass()
        cls.driver.quit()

A template to write your tests:

class SampleTestCase(BaseTestCase):

    def test(self):
    self.driver.get(self.live_server_url)
    # Your test here...

Then just write your tests as usual, with the benefit of having the self.driver directly available.

The following sections contain the test examples of two major features:

  • vote submission,
  • login on admin site.

Test vote submission

The project defines a sample poll ShiningPanda is a... in the polls/fixtures/initial_data.json fixture which is loaded at database initialization.

image

This test verifies that the voting process is working for this poll:

class PollsTestCase(BaseTestCase):

    def test_vote(self):
        self.driver.get(self.live_server_url + '/polls/')
        poll = self.driver.find_element_by_link_text('ShiningPanda is a...')
        poll.click()
        time.sleep(2)    # Should use accurate WebDriverWait
        choices = self.driver.find_elements_by_name('choice')
        self.assertEquals(3, len(choices))
        choices[2].click()
        choices[2].submit()
        lis = self.driver.find_elements_by_tag_name('li')
        self.assertEquals(3, len(lis))
        self.assertEquals('Hosted CI service? -- 0 votes', lis[0].text)
        self.assertEquals('Consulting firm? -- 0 votes', lis[1].text)
        self.assertEquals('Both! -- 1 vote', lis[2].text)
  • First open the page listing the polls: http://localhost:8000/polls/,
  • Search the link pointing to the ShiningPanda is a... poll, and click on it,
  • Verify that three choices are available, then select the last one before submitting the form,
  • Finally verify the vote results on the poll result page.

Test admin site login

The project also defines an administrator admin/admin in the polls/fixtures/initial_data.json fixture which is loaded at database initialization.

image

This test checks that the admin user can log into the admin site:

class AdminTestCase(BaseTestCase):

    def test_login(self):
        self.driver.get(self.live_server_url + '/admin/')
        self.driver.find_element_by_id('id_username').send_keys('admin')
        password = self.driver.find_element_by_id('id_password')
        password.send_keys('admin')
        password.submit()
        time.sleep(2)
        self.assertTrue(self.driver.find_element_by_id('user-tools').text.startswith('Welcome'))
  • First open the administration interface login page: http://localhost:8000/admin/,
  • Then look for the field dedicated to username (has an id id_username) and type admin,
  • Then look for the field dedicated to password (has an id id_password) and type admin before submitting the form,
  • Finally assert that the user is welcomed.

Run tests

To run the test, just execute:

$ python manage.py jenkins
Creating test database for alias 'default'...
..
----------------------------------------------------------------------
Ran 2 tests in 13.018s

OK
Destroying test database for alias 'default'...

What's next?

In our next blog post, how to integrate this project with Jenkins!