Hi everyone! I’ve faced a kind of bug when using django tests and want to share all the information to help to fix or overpass it.
In my case, TestRunner must use only test database, but when I run tests for specific module, testcase made changes in the original database:
python manage.py test --keepdb apps.some_app
But when I run all tests, it uses the test database:
python manage.py test --keepdb
After hours of debugging I found that TestRunner uses the original database, if it’s alias is not specified in testcases that I run. But when I run all tests, in some testcases I have parameter “databases=[‘default’, ‘replica’]”, and everything works fine.
Is it kind of bug, or ‘undeclared feature’?
See all additional information below:
settings.py
DATABASES = {
'default': env.db('DATABASE_URL', default=''),
'replica': env.db('DATABASE_REPLICA_URL', default=''),
...
}
DATABASES['replica']['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': [],
}
test case without database declaration
class TestService(TestCase):
@classmethod
def setUpClass(cls) -> None:
cls._service_class = Service
# NOT OKAY: IT REMOVES DATA IN ORIGINAL DATABASE!
ObjectType.objects.all().delete()
ObjectTypeFactory.create_batch(5)
def test_service(self):
...
test with declared database attribute
class TesStatistic(TestCase):
databases = ['replica', 'default']
@classmethod
def setUpClass(cls) -> None:
# OKAY, IT MAKES CHANGES IN TEST DATABASE
cls.incidents_list: List= StatisticFactory.create_batch(5)
cls.incidents_to_delete: List = StatisticFactory.create_batch(5)
the way to overpass this bug
Create your own TestRunner and re-define setup_databases method 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)