Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test isolation broken with multiple databases in Django. How to fix it?

Django’s TestCase class wraps each test in a transaction and rolls back that transaction after each test, in order to provide test isolation.

Apparently, however, only operations in the default database are within the scope of the transaction. I have a multiple database setup with a router that directs ORM calls on some models to a second database. This means that in the following example, test2 fails:

class MyTestCase(TestCase):

    def test1(self):
        # Let's say Foo model ORM calls are routed to a different DB
        foo = Foo.objects.create(...)
        assert foo.is_ok()

    def test2(self):
        assert not Foo.objects.exists()

The most immediate solution to this problem would be to override the tearDown method in MyTestCase and manually make sure to delete all Foo objects. But this is a bit annoying because it's sort of a hack and database sequences (e.g. autoincrement columns) won't be reset for example, only after the test suite is done and the database is destroyed.

Is there a way to properly fix this, making sure all database operations are by default made inside a transaction and rolled back at the end of each test?

[UPDATE]

Here's my router:

class FooRouter(object):

    def db_for_read(self, model, **hints):

        if model._meta.app_label == 'foo':
            return 'foo_db'

        return None

    def db_for_write(self, model, **hints):

        if model._meta.app_label == 'foo':
            return 'foo_db'

        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):

        if app_label == 'foo':
            return db == 'foo_db'

        return None
like image 322
Ariel Avatar asked Jul 11 '16 12:07

Ariel


2 Answers

As multi_db is deprecated, you can now achieve test with multiple database with the new attribute databases

class YourTestCase(TestCase):
   databases = '__all__'

   def test_something(self):
       pass
like image 182
user3785966 Avatar answered Nov 15 '22 03:11

user3785966


In case you are not using DB Router, you can try to add multi_db = True to TestCase to call flush for all databases

class YourTestCase(TestCase):
   multi_db = True

   def test_something(self):
       pass

Django docs

like image 43
pymen Avatar answered Nov 15 '22 03:11

pymen