Search code examples
pythondjangosqlitedjango-testingdjango-database

Django test database is using local db.sqlite3, not running in memory


When I run Django tests that inserts data into the database, it will insert to my local db.sqlite3 and preserve it when the tests finishes. I don't want this to happen, and it shouldn't be according to the docs:

Regardless of whether the tests pass or fail, the test databases are destroyed when all the tests have been executed.

My unit test:

from unittest import TestCase

from web.constants import USER_TYPE_CONTRACTOR
from web.models import User


class LoginTestCase(TestCase):

    def setUp(self):
        self.demo_user_1_username = 'c2'
        User.objects.create(username=self.demo_user_1_username, password='c12345678')

    def test_user_defaults_to_contractor(self):
        demo_user_1 = User.objects.get(username=self.demo_user_1_username)
        self.assertEqual(demo_user_1.user_type, USER_TYPE_CONTRACTOR)

    def doCleanups(self):
        """Delete demo data from database"""
        # I needed to do this as workaround
        # demo_user_1 = User.objects.get(username=self.demo_user_1_username)
        # demo_user_1.delete()

The user c2 is now in the db.sqlite3, so when I run the test again, it fails as username c2 already exists.

I've tried to do this in settings.py:

DATABASES = {
    'default': dj_database_url.config(conn_max_age=600)
}
DATABASES['default']['TEST'] = {
    'ENGINE': 'django.db.backends.sqlite3',
    'NAME': os.path.join(BASE_DIR, 'test_db.sqlite3'),
}

But test_db.sqlite3 is not created.

How can I use an in-memory sqlite3 db so it doesn't affect my local db when testing?


Solution

  • The problem here, how @Chris mentioned in direct usage of TestCase from unitetest module, the hierarchical chain looks like this:

    TestCase->TransactionTestCase->SimpleTestCase->unittest.TestCase
    

    How documentation summaries:

    [TestCase] - This is the most common class to use for writing tests in Django. It inherits from TransactionTestCase (and by extension SimpleTestCase). If your Django application doesn’t use a database, use SimpleTestCase.

    Here:

    When I run Django tests that inserts data into the database, it will insert to my local db.sqlite3 and preserve it when the tests finishes.

    NOTE: In your case, tests are using the actual database, which leads to a data loss and population with mock data. Never use such workflow!

    Actually, tests mustn't use your actual database at all, by default for sqlite3 backend Django will handle in-memory database, destruction of the database can be controlled using the flag --keepdb. Generally speaking, the flow is as follows:

    1. Test database will be created with the same credential as actual but the name will be test_<actual_db_name>. It is even possible to access the database between test execution in case if it is not deleted every time after tests have passed (--keepdb).
    2. Before test execution, the test DB will be in a state as actual with python manage.py migrate executed, though you can omit migration execution via overriding MIGRATION_MODULES env variable.

    So, the solution is to switch to the django.test.TestCase usage instead.