Skip to content
Advertisement

Django test uses wrong database in some cases

I try to setup my Django tests, and I noticed that when I run all tests TestRunner uses correct test database (for all aliases):

docker-compose exec my_project python manage.py test --keepdb

from django import db
from django.test.runner import DiscoverRunner

from apps.settings.models import Settings


class KeepDBTestRunner(DiscoverRunner):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.verbosity = 2
        self.keepdb = True

        self.db_base_name = 'test_postgresdevuser'

    def run_checks(self):
        super().run_checks()
        print(db.connections.databases)

----------------------------------------------
result
----------------------------------------------
{'default': {'NAME': 'test_postgresdevuser', 'USER': 'user', 'PASSWORD': '*******', 'HOST': 'postgres', 'PORT': 5432, 'ENGINE': 'apps.core.db_backends.open', 'TEST': {'SERIALIZE': False, 'DEPENDENCIES': [], 'CHARSET': None, 'COLLATION': None, 'NAME': None, 'MIRROR': None}, 'ATOMIC_REQUESTS': False, 'AUTOCOMMIT': True, 'CONN_MAX_AGE': 0, 'OPTIONS': {}, 'TIME_ZONE': None}, 

'vk_master': {'NAME': 'test_postgresdevuser', 'USER': 'user', 'PASSWORD': '*******', 'HOST': 'postgres', 'PORT': 5432, 'ENGINE': 'apps.core.db_backends.open', 'TEST': {'SERIALIZE': False, 'DEPENDENCIES': [], 'CHARSET': None, 'COLLATION': None, 'NAME': None, 'MIRROR': None}, 'ATOMIC_REQUESTS': False, 'AUTOCOMMIT': True, 'CONN_MAX_AGE': 0, 'OPTIONS': {}, 'TIME_ZONE': None},


But when I run tests for specific module, it uses the original database:

docker-compose exec my_project python manage.py test --keepdb apps.my_module

...
print(db.connections.databases)

----------------------------------------------
result
----------------------------------------------
{'default': {'NAME': 'postgresdevuser', 'USER': 'user', 'PASSWORD': '*******', 'HOST': 'postgres', 'PORT': 5432, 'ENGINE': 'apps.core.db_backends.open', 'TEST': {'SERIALIZE': False, 'DEPENDENCIES': [], 'CHARSET': None, 'COLLATION': None, 'NAME': None, 'MIRROR': None}, 'ATOMIC_REQUESTS': False, 'AUTOCOMMIT': True, 'CONN_MAX_AGE': 0, 'OPTIONS': {}, 'TIME_ZONE': None}, 

'vk_master': {'NAME': 'postgresdevuser', 'USER': 'user', 'PASSWORD': '********', 'HOST': 'postgres', 'PORT': 5432, 'ENGINE': 'apps.core.db_backends.open', 'TEST': {'SERIALIZE': False, 'DEPENDENCIES': [], 'CHARSET': None, 'COLLATION': None, 'NAME': None, 'MIRROR': None}, 'ATOMIC_REQUESTS': False, 'AUTOCOMMIT': True, 'CONN_MAX_AGE': 0, 'OPTIONS': {}, 'TIME_ZONE': None},

What is the reason of such behavior? Using the original database is not allowed when I run tests on pre-production stages!

Project configuration:

Django=3.0.7

*.env* file:
DATABASE_URL=postgres://user:********@postgres:5432/postgresdevuser
...

*settings.py* file:
DATABASES = {
    'default': env.db('DATABASE_URL', default=''),
    'vk_master': env.db('DATABASE_URL', default=''),
...
}
DATABASES['vk_master']['ENGINE'] = 'apps.core.db_backends.open'
DATABASES['default']['ENGINE'] = 'apps.core.db_backends.open'

for db_name in DATABASES:
    DATABASES[db_name]['TEST'] = {
        'SERIALIZE': False,
        'DEPENDENCIES': [],
    }
...

I would be grateful for any advice!

Advertisement

Answer

I think it is a kind of bug in Django test. When there is no ‘databases’ parameter in any TestCase, it does not use the test database, but uses the original one. To overpass this, I redefined setup_databases method in my TestRunner like this:

class KeepDBTestRunner(DiscoverRunner):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.keepdb = True

    def setup_databases(self, **kwargs):
        
        # define aliases here. In original class aliases will be 'None' (from calling 
        # self.get_databases(suite)) and it causes a problem.

        aliases = {'default', 'replica'}  
        return super().setup_databases(aliases=aliases)

Advertisement