Testing DJANGO




Code Fellows
March 18, 2014

Who am I?



@joulespersecond

What we'll cover


Testing philosophies
Writing model tests
Writing view tests
Front-end testing with Selenium

Why test?


It saves you time (in the long run)
It's more accurate
It shows you care about code quality

Why test?

Jacob Kaplan-Moss, ex-BDFL Django
"Code without tests is broken by design."

NOTES APP

https://github.com/paulcwatts/codefellows-django-testing

What's wrong?

Let's write some tests!

Writing Views tests


Writing Views Tests

Useful TestCase methods:

assertTemplateUsed(response, template_name)
assertContains(response, text, html=True|False)
assertRedirects(response, url)
assertRaises(exception)

More:

The DJango Test Runner


python manage.py test 

Creates a temporary test database

Finds files starting with "test" *

Finds classes in that file deriving from TestCase

Finds methods in that class starting with "test_"

* Recently changed in Django 1.6

THE DJANGO TEST RUNNER

Running all tests:

python manage.py test

Running tests from one app

python manage.py test notes

Running tests from one class:

python manage.py test notes.tests.ViewTest

Running a single test:

python manage.py test notes.tests.ViewTest.test_empty_list

Test driven development

It's easier than you think.

Test Driven Development


It's as simple as:

  1. Write the test
  2. Watch the code fail
  3. Fix the code

TEST DRIVEN DEVELOPMENT

Don't we want the notes to be latest first?

Write the test first


class ModelsTest(TestCase):
    def test_ordering(self):
        "Notes are ordered most recent first."
        now = datetime.utcnow()

        # We want to be deliberate about the timestamps, so we're not
        # at the mercy of the test timing.
        note1 = Note.objects.create(note='Older note', timestamp=now - timedelta(days=1))
        note2 = Note.objects.create(note='Newer note', timestamp=now)
        self.assertQuerysetEqual(Note.objects.all(), [note2.pk, note1.pk],
                                 transform=lambda note: note.pk)


Run the test


Error
Traceback (most recent call last):
  File "/Users/paulw/Work/me/codefellows-django-testing/notes/tests/test_models.py", line 18, in test_ordering
    transform=lambda note: note.pk)
  File "/Users/paulw/.virtualenvs/codefellows/lib/python2.7/site-packages/django/test/testcases.py", line 827, in assertQuerysetEqual
    raise ValueError("Trying to compare non-ordered queryset "
ValueError: Trying to compare non-ordered queryset against more than one ordered values

Fix the code


class Note(models.Model):
    note = models.CharField(max_length=2000)
    timestamp = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ('-timestamp',)

    def get_absolute_url(self):
        return reverse('note-detail', kwargs={'pk': self.pk})

    def __unicode__(self):
        return self.note

browser testing with Selenium


When you can't test using Django


JavaScript-heavy sites


Single page applications

BROWSER TESTING: MISSION

Don't we want the <textarea> to automatically  focus?

LiveServerTestCase

class BrowserTest(LiveServerTestCase):
    @classmethod
    def setUpClass(cls):
        cls.selenium = getattr(webdriver, WEBDRIVER_CLASS_NAME)()
        super(BrowserTest, cls).setUpClass()

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

    def test_autofocus_add(self):
        "Tests that the textarea in the add form is autofocused."
        self.selenium.get('{0}{1}'.format(self.live_server_url, reverse('note-add')))
        textarea = self.selenium.find_element_by_name("note")
        active = self.selenium.switch_to_active_element()
        self.assertEqual(textarea, active,
                         "textarea should be focused, but {0} is instead".format(
                             active.tag_name
                         ))

Run the test


Traceback (most recent call last):
  File "/Users/paulw/Work/me/codefellows-django-testing/notes/tests/test_browser.py", line 39, in test_autofocus_add
    active.tag_name
AssertionError: textarea should be focused, but body is instead

Fix the code


 class NoteForm(forms.ModelForm):     note = forms.CharField(widget=forms.Textarea(attrs={'autofocus': ''}))

Testing packages with tox

Useful when creating reusable packages

Tests setup.py

Easily test against different environments/Django versions

MOAR TESTING

Python 3.3 support!

Formatting checking with PEP8/flake8

Links

Django docs:

Selenium:

Tox:

One more thing...

We're HIRING

Awesome culture

Django backend
Ember.js frontend
Mobile
Work for startups
paul@joltlabs.com

Thank you!


paul@joltlabs.com
@joulespersecond
@paulcwatts

Q&A

Testing Django

By Paul Watts

Testing Django

My slides for the "Testing Django" talk at Code Fellows. Example code: https://github.com/paulcwatts/codefellows-django-testing

  • 1,306