personal web log written by izabeera and dryobates

testing django-smarttest TDD splinter


by dryobates

If you want do Test Driven Development your tests should run in a split second. Your tests shouldn't touch database if it's possible nor you should run slow acceptance tests in one run with unit tests for TDD. django-smarttest is package which helps you keep with that rules.

In django-smarttest [1] there are two decorators which help you keep your tests fast. In the same vein package contains TestCase subclass for running splinter [2] with different drivers. The fastest for normal TDD and slower ones which can run real browser.

no_db_testcase decorator

no_db_testcase decorator is security fence that separates your tests from database. If used as decorator on test method or TestCase class it would raise RunTime error if your decorated tests touch database in any time. Unit tests shoudn't touch database at all unless you test database queries. Most of my tests don't need to check database so I put decorator on them like that:

from smarttest.decorators import no_db_testcase

class FizzBuzzTestCase(TestCase):

     def test_should_return_fizz_on_3(self):

And if I touch database by accident I'll get exception:

ERROR: test_should_return_fizz_on_3 (fizzbuzz.tests.FizzBuzzTestCase)
Traceback (most recent call last):
RuntimeError: Do not touch the database!

Ran 12 tests in 0.054s

FAILED (errors=1)

Such error is good indicator that I have missed to mock something in my tests.

test_type decorator

The other decorator allows you to mark tests as some type of tests. Generally I use two types of tests: unit tests and acceptance tests. When using double loop I like to disable running acceptance tests untill I'll finish all unit tests for given scenario and then run all of them at once. That method allows me running tests continuously while doing TDD as my unit tests are fast while acceptance aren't.

Just like with previous decorator test_type can be used on single test methods and on whole TestCase subclasses.

from smarttest.decorators import test_type

class FizzBuzzTestCase(TestCase):

     def test_should_return_fizz_on_3(self):

And you can set up IGNORE_TESTS environment variable to ignore tests marked with "acceptance":

$ IGNORE_TESTS=acceptance python test
Creating test database for alias 'default'...
Ran 12 tests in 0.030s

OK (skipped=1)

SplinterTestCase class

Last addition is SplinterTestCase. Splinter [2] gives you consistent api for manipulating different drivers. You can instruct your browser (e.g. "firefox" or "chrome") to simulate user behaviour. When you run tests on CI machine you often don't have real browser so you can switch your driver to some headless one (e.g. "phantomjs", "zope.testbrowser" or the fastest one "django"). SplinterTestCase is thin wrapper arround django TestCase that allowes you running different splinter drivers just switching them in

TEST_DRIVER = 'firefox'

In tests:

from smarttest.testcases import SplinterTestCase

class FizzBuzzTestCase(SplinterTestCase):

     def test_should_return_fizz_on_3(self):
         self.browser.visit(self.get_host() + reverse('index'))

SplinterTestCase instance has attribute "browser" which you use to instruct browser and method "get_host" which returns correct host name for given driver.

[2](1, 2) splinter
Jakub Stolarski. Software engineer. I work professionally as programmer since 2005. Speeding up software development with Test Driven Development, task automation and optimization for performance are things that focus my mind from my early career up to now. If you ask me for my religion: Python, Vim and FreeBSD are my trinity ;) Email:


Tag cloud