27

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="mail@gmail.com",password="welcome")        


        def test_starting_an_exam_view(self):               
            candidate = Candidate.objects.get(email="mail@gmail.com")
            .......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?

Joel Crocker
  • 155
  • 5
Rama Vadakattu
  • 1,266
  • 2
  • 16
  • 24

7 Answers7

19

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.

mhost
  • 6,930
  • 5
  • 38
  • 45
  • +1 for a functional solution that actually leaves room for a different approach than using initial_data.json/yaml. (I use a custom management command and a script that calls the Django ORM, to create my initial data. This lets me integrate that perfectly.) This should be the accepted answer, it helped me out a lot! – floer32 Aug 27 '12 at 22:50
  • 1
    Great solution! One thing that I've discovered that's quite useful. If you don't put in the def _fixture_setup(self) method, and allow the Django TestCase to tear down as usual, any database changes made within one test case will not effect the next one. This is because at the start of a test case, a database transaction is started, and during the tear down phase the test case will perform a db rollback to reset the state of the database. So ignoring the def _fixture_setup() will give you the bets of both worlds! – LeeMobile Mar 20 '13 at 12:35
12

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...
Tom Wainwright
  • 648
  • 1
  • 6
  • 15
  • 1
    This only works if every test on the module uses that same fixture. In other words, it forces you to put tests on a given module by what fixture they use, instead of what they test. – Jorge Leitao Oct 11 '15 at 06:30
9

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.

oogles
  • 1,202
  • 1
  • 20
  • 29
7

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

Community
  • 1
  • 1
kerryz
  • 361
  • 3
  • 10
  • Note, if using this approach, you need to override both _fixture_setup and _fixture_teardown so that the fixtures you loaded in setupmodule are not trashed by the fixture teardown. – n00b Aug 27 '19 at 20:44
  • Also note, this approach does not reset auto-incrementing primary keys/ids; thus you should not count on the IDs starting (or restarting) at one in your test. – n00b Aug 28 '19 at 19:31
3

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.

jamesls
  • 5,428
  • 1
  • 28
  • 17
  • Thanks for the pointer,& Yes ( i did not thought about this earlier) we can use intial_data only if every test case don't modify the database – Rama Vadakattu Jun 11 '09 at 06:57
  • i decided to use unittest.TestCase with intial_data . Any idea on how to get all the conveniences provided by django.test.TestCase? – Rama Vadakattu Jun 11 '09 at 07:49
1

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.

Manoj Govindan
  • 72,339
  • 21
  • 134
  • 141
0

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.

Joel Crocker
  • 155
  • 5