Search code examples
pythonpython-3.xdjangodjango-testingdjango-tests

In python Django how to define test database and keep records inserted in test database until cleaned in tearDown method of testcase


I want a test database created for my default database in Django latest version, for that I configured in project settings.py file as below.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
        'TEST': {
            'NAME': BASE_DIR / 'test_db.sqlite3',
        }
    }
}

I have a model Topic in my models.py file and have run makemigrtions & migrate cmds to have table in sqlite db. I have created TestTopic class in my app(first_app) tests.py file as below

from django.test import TestCase,SimpleTestCase
from .models import Topic

class TestTopic(TestCase):
    def setUp(self):
        Topic.objects.create(top_name='Test topic1')
        Topic.objects.create(top_name='Test topic2')
        Topic.objects.create(top_name='Test topic3')

    def test_create_topic(self):
        all_topics = Topic.objects.count()
        self.assertEqual(all_topics, 3)

    def tearDown(self) -> None:
        return super().tearDown()

I am executing cmd below to run my app(first_app) tests. Passing --keepdb for preserving test_db.

py manage.py test first_app --keepdb

After this I see test case pass. But when I am checking test_db.sqlite in sqlite db browser app to see test topics I created in test case, I do not see them. I want to have those records in test_db until I clear them manually or in tearDown method of my test case. ( Below is sqlite db browser of test_db.sqlite topics table.

test_db.sqlite


Solution

  • the -keepdb option will only keep the schema of the database but not the data inside. The data created on each test is never committed to the database, Django creates a database transaction before executing one test method and at the end of the test that transaction gets rolled back if you're using the TestCase class.

    A TestCase, on the other hand, does not truncate tables after a test. Instead, it encloses the test code in a database transaction that is rolled back at the end of the test. This guarantees that the rollback at the end of the test restores the database to its initial state. (It's not a good idea to save data on the database during the tests, that migh break the idempotence of the tests. Meaning that you might get different results if you execute the same test twice)

    if you use the setUpTestData then the data will be preserved during the whole set of tests of the class:

    class TestFoo(TestCase):
        @classmethod
        def setUpTestData(cls):
            User.objects.create(...)
            User.objects.create(...)
            User.objects.create(...)
    
        def test_something(self):
            self.assertEqual(1 + 1, 2)
    
        def tearDown(self) -> None:
            print(User.objects.count())  # you'll have the 3 users here
            return super().tearDown()