4

I'm trying to load a json fixture into a Python 2.6/Django 1.4 unit test. I can do it at the test level, using Django's TestCase, but it takes 8-10 seconds to load (2M of json, not going to get much smaller). Running a dozen tests is therefore really slow, and I'd like to load the test database just once. It looks like it might be possible in Python 2.6, with the addition of unittest2, but I haven't gotten it to work.

# Works, but takes eight seconds per test.
class BaseStuff(django.test.testcases.TestCase):
    fixtures = ['test_data']

    def setUp(self):
        # stuff
    def test_one(self):
        # stuff

# Doesn't work - but runs *really* fast.
class BaseStuff(unittest2.TestCase):
    @classmethod
    def setUpClass(cls):  # Added in unittest2
        fixtures = ['test_data']
        print "in setupClass()"  # Does print, so function is called.

Trying setupModule() didn't work, either. The setup function is definitely being called, but doesn't seem to be fixture-aware.

So Django's TestCase will read fixtures, and unittest2's TestCase will do things at the class/module level. Is there any way to combine this operation, and read fixtures at the class/module level? I tried multiple inheritance, and couldn't get it to load the data.

Update: Based on @robjohncox's suggestion, this code, at the module level:

from django.core import management
management.call_command('loaddata', 'test_data.json', verbosity=1, noinput=True)

does seem to create a database. But then Django appears to create another database for each test (which is empty). Not sure how to tell the TestCase to use the first db (I tried both the Django and unittest2 TestCase).

John C
  • 6,285
  • 12
  • 45
  • 69

2 Answers2

1

You may be able to solve this by loading the fixture data manually yourself inside setUpClass method - there is a django admin command (loaddata) which can do this for you which can be called from the code. As you correctly pointed out in your example, this would need to be a unittest2.TestCase.

Community
  • 1
  • 1
robjohncox
  • 3,639
  • 3
  • 25
  • 51
  • Any more information on usage? I tried setting `--database=test_foo`, and got `connection test_foo does not exist` (the database with that name does exist). Same results when using `loaddata` on the command line, versus in code. – John C Jan 20 '14 at 13:36
  • I think the problem is that, because you aren't using a django TestCase, the database isn't explicitly created when the tests are run. I think you can work around this for your case by calling the `syncdb` command (with argument `--noinput`) in your `setUpClass` method before running `loaddata`. This isn't a technique I have tried before myself, but it sounds like it should work. Hope this helps. – robjohncox Jan 20 '14 at 13:44
  • Actually, I was using a Django TestCase (hadn't changed the code), and I tried loading both at the top of the module, and inside the TestCase. Will try syncdb - results, no joy... Hm, now I'm wondering if it's looking for the db name in settings.py. More testing... – John C Jan 20 '14 at 13:49
  • Sorry to hear that didn't work for you :( Will delete the answer once someone can provide you with something that works. Best of luck. – robjohncox Jan 20 '14 at 13:53
  • Not saying your answer is wrong, just that it's more of a hint. :) Didn't know if you had used this method, and might have more information or not. I'm actually making progress - after setting a fake name, and not setting a db name, it runs on the command line. Just not yet in code. – John C Jan 20 '14 at 14:20
0

As Django doc suggests, you can use the name of the DB in the name of the fixture, to load the data in a db:

So combining your suggestion and mine:

In your settings.py, you have this databases:

DATABASES = {'default': ..., 'legacy_db': ...}

Then you can load fixture to a db like this:

from django.core import management
management.call_command('loaddata', 'test_data.legacy_db.json', verbosity=1, noinput=True)

Reference: https://docs.djangoproject.com/en/dev/ref/django-admin/#database-specific-fixtures

Gonzalo
  • 752
  • 8
  • 23