Getting started with Django testing Django 16.12.2013

You maybe alredy familiar with two well known types of tests: unit tests and integration tests. Unit tests are used for test a very small unit of code, such as a single method or function. Integration tests are used for whole application, from invocation to response. They check whether the tested units behave correctly as a group.

In big project tests are good habit. Without properly testing your code, you will never know if the code works as it should. If you employ test you will have more confidence in your code.

We will test models, views and forms at django app. The test app has following structure:

  • root of app: __init__.py, models.py, views.py, forms.py;
  • test directory: __init__.py, test_models, test_views.py, test_forms.py.

Model to test

# vim models.py
from django.db import models

class Animal(models.Model):
    title = models.CharField('Title', max_length=50)
    weight = models.PositiveIntegerField('Weight')

    def __unicode__(self):
        return self.title

We will use the following settings for our test. It uses sqlite3 and disabled migrations for performance.

from settings import *
COMPRESS_ENABLED = False

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3'
    }
}

SOUTH_TESTS_MIGRATE = False

Test models

# vim test/test_models.py

from django.test import TestCase
from animals.models import Animal

class AnimalTest(TestCase):
    def test_animal_creation(self):
        animal = Animal.objects.create(title='Lion', weight=65)
        self.assertTrue(isinstance(animal, Animal))
        self.assertEqual(animal.__unicode__(), animal.title)

Run test

python manage.py test --settings=dj16.test_settings animals

Test views

Simple test view for HTTP success status and appereance text in page

# vim test/test_views.py

from django.test import TestCase
from django.core.urlresolvers import reverse
from animals.models import Animal

class AnimalTest(TestCase):
    def test_animal_list_view(self):
        animal = Animal.objects.create(title='Lion', weight=65)
        url = reverse('animals')
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        self.assertIn(animal.title, response.content)        
        #self.assertContains(response, animal.title)
        #self.assertQuerysetEqual(response.context['animals'], [''])

Run test

python manage.py test --settings=dj16.test_settings animals -v 2

Test application functionality with selenium

# vim test/test_views.py

from django.test import LiveServerTestCase
from django.core.urlresolvers import reverse
from selenium.webdriver.firefox.webdriver import WebDriver
from animals.models import Animal

class AnimalSeleniumTest(LiveServerTestCase):
    @classmethod
    def setUpClass(cls):
        cls.selenium = WebDriver()
        super(AnimalSeleniumTest, cls).setUpClass()

    @classmethod
    def tearDownClass(cls):
        cls.selenium.quit()
        super(AnimalSeleniumTest, cls).tearDownClass()

    def test_animal_add_view(self):
        self.selenium.get(self.live_server_url + reverse('animal_add'))
        self.selenium.find_element_by_id('id_title').send_keys('Lion')
        self.selenium.find_element_by_id('id_weight').send_keys('57')
        self.selenium.find_element_by_id('btn_submit').click()

        self.selenium.get(self.live_server_url + reverse('animals'))
        ul = self.selenium.find_element_by_tag_name('ul')
        self.assertIn('Lion', ul.text)

Run test

python manage.py test --settings=dj16.test_settings --liveserver=localhost:8000 animals -v 2

Test forms

# vim test/test_forms.py

from django.test import TestCase
from animals.models import Animal
from animals.forms import AnimalAddForm

class AnimalTest(TestCase):
    def test_animal_valid_form(self):
        animal = Animal.objects.create(title='Lion', weight=65)
        data = {'title': animal.title, 'weight': animal.weight}
        form = AnimalAddForm(data=data)
        self.assertTrue(form.is_valid())

Coverage

Install coverage and add coverage to your INSTALLED_APPS

pip install coverage

Run coverage

coverage run manage.py test animals -v 2

Build report to see what remain to test

coverage html --include='animals/*'

or in terminal

coverage report --include='animals/*'