One of the design decisions I made when writing badrabbit was to make it fully compatible with unittest (one of the two unit testing frameworks that come with the python stdlib). My reasoning was twofold, firstly to minimize the amount of disruption caused by having to learn a new API and secondly, to give folks a chance to integrate badrabbit‘s mocking library and test decorator syntax in to existing tests with a minimum of fuss.
This decision paid off recently when I tried integrating badrabbit unit tests into a django application. Django has built in support for running unit tests as part of the development lifecycle. The manage.py script that gets generated when you run django’s equivalent of Rails’ scaffolding, takes one of a several commands and one of these is ./manage.py test <appname>.
This has the effect of going off a running all your tests, where the tests for an application are either in your model definition (models.py) a file named tests.py, or contained in (multiple) modules in a package of the same name. By default as described here, django will go off and look for tests defined with the unittest and doctest frameworks, both of which are part of python‘s stdlib. You can override this behavior by providing your own test runner and doing so is, in fact, ridiculously easy. Nevertheless, this isn’t neccessary because badrabbit is 100% compatible with unittest. And so all you need do to use it in your tests is put and import statement in your script, subclass badrabbit.testmagic.autotest instead of unittest.TestCase and you’re off. Here’s an abridged example:
#!/usr/bin/env python import badrabbit from badrabbit import * from users.models import ServiceUser class ServiceUserTests(autotest): @test def it_should_set_absolute_uri_correctly(self): user = ServiceUser(username='Johannes') assert_that(user.get_absolute_url(), equal_to("/users/Johannes/"))
Of course, django has its own subclass of unittest.TestCase already, making subclassing badrabbit.testmagic.autotest less attractive (not least since they share the same superclass!) so there is another way. If you were to go take a peek at the implementation of badrabbit‘s autotest class you would soon notice that it does nothing apart from setting the metaclass to AutoTest:
class AutoTestCase(unittest.TestCase): __metaclass__ = AutoTest autotest = AutoTestCase
So instead of subclassing autotest you can just as well set the metaclass (in exactly this way) on your own test class which can then happily subclass django’s TestCase instead.
I can’t imagine the integration being much simpler really. Because badrabbit depends on hamcrest-python, which doesn’t appear to come with an installer, we stuck it a couple of directories above our code and run the manage.py utility using a wrapper shell script which configures the PYTHON_PATH environment variable to include this first.