3. How do you know?
» First 4 months of my job was porting and
testing Ellington
» Going from Django r1290 to Django 1.0.
» Suite from 0 to 400 tests. (Now has 1715)
4. 30,000 Ft View
» State of testing in Django
» Why you should be testing
» How you start testing
» Useful tools
» Eventual Goals
9. Basic Test
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.failUnlessEqual(1 + 1, 2)
__test__ = {"doctest": """
Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2
True
"""}
27. Awesome Documentation
def parse_ttag(token, required_tags):
"""
A function to parse a template tag.
It sets the name of the tag to 'tag_name' in the hash returned.
>>> from test_utils.templatetags.utils import parse_ttag
>>> parse_ttag('super_cool_tag for my_object as obj', ['as'])
{'tag_name': u'super_cool_tag', u'as': u'obj'}
>>> parse_ttag('super_cool_tag for my_object as obj', ['as', 'for'])
{'tag_name': u'super_cool_tag', u'as': u'obj', u'for': u'my_object'}
"""
bits = token.split(' ')
tags = {'tag_name': bits.pop(0)}
for index, bit in enumerate(bits):
bit = bit.strip()
if bit in required_tags:
if len(bits) != index-1:
tags[bit] = bits[index+1]
return tags
28. Awesome Documentation
>>> from test_utils.templatetags.utils import parse_ttag
>>> parse_ttag('super_cool_tag for my_object as obj', ['as'])
{'tag_name': u'super_cool_tag', u'as': u'obj'}
>>> parse_ttag('super_sweet for object as obj', ['as', 'for'])
{'tag_name': u'super_sweet', u'as': u'obj', u'for': u'object'}
31. Basic Unit Test
import random
import unittest
class TestRandom(unittest.TestCase):
def setUp(self):
self.seq = range(10)
def testshuffle(self):
# shuffled sequence does not lose any elements
random.shuffle(self.seq)
self.seq.sort()
self.assertEqual(self.seq, range(10))
if __name__ == '__main__':
unittest.main()
33. Test Client
» Test HTTP Requests without server
» Test Views, Templates, and Context
34. Django’s TestCase
from django.contrib.auth.models import User
from django.test import TestCase
from django.core import mail
class PasswordResetTest(TestCase):
fixtures = ['authtestdata.json']
urls = 'django.contrib.auth.urls'
def test_email_not_found(self):
"Error is raised if the provided email address isn't currently
registered"
response = self.client.post('/password_reset/', {'email':
'not_a_real_email@email.com'})
self.assertEquals(len(mail.outbox), 0)
40. Browser tests
» Run tests in a web browser
» Check compatibility of design
» Basically an IE sanity check
» Only real way to test JS, AJAX, CSS
» Slow
53. Making Functional tests
» Usually a relatively annoying process
» Testmaker makes it easy.
» ./manage.py testmaker [app]
» Simply browse and your session is
recorded.
61. Test-Only Models
#test_models.py
from django.db import models
class TestModel(models.Model):
name = models.CharField()
#tests.py
from django.test import TestCase
class TestMyViews(TestCase):
test_models = ['test_models']
def testIndexPageView(self):
from myapp.models import TestModel
TestModel.objects.get(name='daniellindsleyrocksdahouse')
62. Skipping Tests
» Views Required
» Models Required
» Specific database type
» Conditional on a Function
63. Skipping Tests
from django.tests.decorators import conditional_skip
import datetime
class TestUnderCondition(TestCase):
def _check_2009():
# Condition returning True if test should be run and False if it
# should be skipped.
if datetime.datetime.now() > datetime.datetime(2009, 01, 01):
return True
@conditional_skip(_check_2009, reason='This test only runs in 2009')
def testOnlyIn2009(self):
# Test to run if _my_condition evaluates to True
64. Skipping Tests
======================================================================
SKIPPED: test_email_found (django.contrib.auth.tests.basic.PasswordResetTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/dnaquin/Dropbox/Sandbox/django/django/test/decorators.py", line 43, in _skip
raise SkippedTest(reason=reason)
SkippedTest: Required view for this test not found: django.contrib.auth.views.password_reset
----------------------------------------------------------------------
Ran 408 tests in 339.663s
FAILED (failures=1, skipped=2)
65. Coverage
» Basically the hotness
» django-admin.py test --coverage
» django-admin.py test --coverage --report