As mentioned in yet-unresolved #7835, I've been writing a sort of meta-application recently that doesn't provide any models of its own but absolutely requires models (and data) of various types to test against. I ended up adopting julien's technique, which puts the test-only application inside the
tests module, and then overrides
tearDown to monkey patch your settings to include the test-only application and then run a
syncdb command. This approach started out working very well, but I soon ran into a fairly major problem:
fixtures failed to load.
I had by this time created a few subclasses of Django's
TestCase object, using
super to call up the chain. But my fixtures were nowhere to be found when I dropped into pdb. After messing around a bit with pdb (and looking at all the wrong stuff), I took a look at the actual definition of django's TestCase.
def __call__(self, result=None): """ Wrapper around default __call__ method to perform common Django test set up. This means that user-defined Test Cases aren't required to include a call to super().setUp(). """ self.client = Client() try: self._pre_setup() # snip exception handling... super(TransactionTestCase, self).__call__(result) try: self._post_teardown() # snip exception handling...
The way that your tests are run are roughly:
- the testrunner looks for all
unittest.TestCasesubclasses in your
- the testrunner instantiates them (
__init__), then calls them (
TestCaseattaches the test client, runs its own
_pre_setupmethod, then runs
- your test is run as usual (
The problem with my code (and julien's in the links at the top) is that we're overriding
setUp to install the test-only application, which is called after Django's
_pre_setup, which is where the fixture loading is done. The lesson is that, if you want to create your own test baseclass and you are going to be messing with Django's settings in some way that'l affect the database, you want to reimplement
_post_teardown in your subclass.
Here's an example of a BaseTestCase class that will install a test-only application and set DEBUG to True:
from django.test import TestCase class BaseTestCase(TestCase): def _pre_setup(self): self.saved_INSTALLED_APPS = settings.INSTALLED_APPS self.saved_DEBUG = settings.DEBUG test_app = 'foo.tests.testapp' settings.INSTALLED_APPS = tuple( list(self.saved_INSTALLED_APPS) + [test_app] ) settings.DEBUG = True # load our fake application and syncdb load_app(test_app) call_command('syncdb', verbosity=0, interactive=False) super(BaseTestCase, self)._pre_setup() def _post_teardown(self): settings.INSTALLED_APPS = self.saved_INSTALLED_APPS settings.DEBUG = self.saved_DEBUG super(BaseTestCase, self)._post_teardown()