27
votes

In unit tests I need to load fixtures, as below:

   class TestQuestionBankViews(TestCase):

        # Load fixtures
        fixtures = ['qbank']

        def setUp(self):                           
            login = self.client.login(email="[email protected]",password="welcome")        


        def test_starting_an_exam_view(self):               
            candidate = Candidate.objects.get(email="[email protected]")
            .......etc


        def test_review_view(self):
            self.assertTrue(True)            
            .........

       def test_review_view2(self):
            self.assertTrue(True)
            .........

Problem:

These fixtures are loading for every test, i.e. before test_review_view, test_review_view2, etc., as Django flushes the database after every test.

This behaviour is causing tests to take a long time to complete.

How can I prevent this redundant fixture loading?

Is there a way to load fixtures in setUp and flush them when the test class is finished, instead of flushing between every test?

7
oh......... i think i can resolve this by using intial_data fixture and inheriting "unittest.Testcase" instead of "test.TestCase"? Any other thoughts?Rama Vadakattu

7 Answers

20
votes

Using django-nose and a bit of code, you can do exactly what you asked for. With django-nose, you can have per-package, per-module and per-class setup and teardown functions. That allows you to load your fixtures in one of the higher-up setup functions and disable the django.test.TestCase's resetting of the fixtures between tests.

Here is an example test file:

from django.test import TestCase
from django.core import management

    def setup():
        management.call_command('loaddata', 'MyFixture.json', verbosity=0)

    def teardown():
        management.call_command('flush', verbosity=0, interactive=False)

    class MyTestCase(TestCase):

        def _fixture_setup(self):
            pass

        def test_something(self):
            self.assertEqual(1, 1)

Notice that setup and teardown are outside of the class. The setup will be run before all the test classes in this file, and the teardown will be run after all test classes.

Inside the class, you will notice the def _fixture_setup(self) method. This overrides the function that resets the database in between each test.

Keep in mind that if your tests write anything to the database, this could invalidate your tests. So any other tests that need fixtures reloaded for each test should be put in a different test file.

13
votes

Or use setUpModule:

def setUpModule():
    print 'Module setup...'

def tearDownModule():
    print 'Module teardown...'

class Test(unittest.TestCase):
    def setUp(self):
       print 'Class setup...'

    def tearDown(self):
       print 'Class teardown...'

    def test_one(self):
        print 'One'

    def test_two(self):
        print 'Two'

prints:

Creating test database for alias 'default'...
Module setup...
Class setup...
One
Class teardown...
Class setup...
Two
Class teardown...
Module teardown...
9
votes

For what it's worth, and since there's no accepted answer, Django 1.8 now provides this functionality out of the box - provided you're using a database backend that supports transactions.

It also adds the TestCase.setUpTestData() method for the manual creation of test data once per TestCase class.

See the Django 1.8 release notes.

8
votes

If you don't feel like installing a new package just for this purpose, you can combine Tom Wainwright's solution and mhost's solution.

In your testfile, add these functions outside of any classes:

from django.core.management import call_command

def setUpModule():
    call_command(
        'loaddata', 
        'path_to_fixture.json',
        verbosity=0
    )

def tearDownModule():
    call_command('flush', interactive=False, verbosity=0)

If you don't want to have these fixtures loaded into the database for all test cases, split the test into multiple files by creating a new directory in the app called tests, add an empty __init__.py file to tell Python that this is a package, and add your test files with file names that begin with test, since the runner will look for files matching the pattern test*.py

3
votes

I've ran into the same problem. In general, there isn't a really good way to do that using django's test runner. You might be interested in this thread

With that being said, if all the testcases use the same fixture, and they don't modify the data in any way, then using initial_data would work.

1
votes

I had a similar problem once and ended up writing my own test runner. In my case initial_data was not the right place as initial_data would be loaded during syncdb, something I did not want. I overrode setup_ and teardown_test_environment methods to load my custom fixture before the test suite was run and to remove it once done.

0
votes

django-nose provides a readymade solution to this problem: simply subclass django_nose.FastFixtureTestCase.

Additionally, django-nose supports fixture bundling, which can speed up your test runs even more by only loading each unique set of fixtures once per test run. After having subclassed FastFixtureTestCase where appropriate, run the django-nose test runner using the --with-fixture-bundling option.

See django-nose on pypi for more information.