'Inherinting Django SetUpTestData method called for each child

Inheriting a mother class make its classmethod setUpTestData called for each child class. This is what I excepted but not what I want.

Here is a minimalist example

from django.test import TestCase


class ParentClass(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.parent_attr = "parent value" 
        print("ParentClass.setUpTestData called") # this is called twice


class TestChild1(ParentClass):
    @classmethod
    def setUpTestData(cls):
        super(TestChild1, cls).setUpTestData()
        cls.child_attr = "child value 1"

    def test_child(self):
        self.assertEqual(self.parent_attr, "parent value")
        self.assertEqual(self.child_attr, "child value 1")


class TestChild2(ParentClass):
    @classmethod
    def setUpTestData(cls):
        super(TestChild2, cls).setUpTestData()
        cls.child_attr = "child value 2"

    def test_child(self):
        self.assertEqual(self.parent_attr, "parent value")
        self.assertEqual(self.child_attr, "child value 2")

$ python manage.py test accounts.tests.test_test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
ParentClass.setUpTestData called
.ParentClass.setUpTestData called
.
----------------------------------------------------------------------
Ran 2 tests in 0.006s

OK
Destroying test database for alias 'default'...

I want to be able to make many child class where each child class would apply tiny modification to the common 'inherited' database. However I do not want to run many time the parent classmethod as it is very slow.

How can I ensure parent database is generated only once and that every child class works on a copy of parent database instead of regenerating the entire database.



Solution 1:[1]

As you have stated yourself and @brian-destura pointed out as well, your best option to share data between test classes would be implementing your own test runner.

The runner should inherit from django.test.runner.DiscoverRunner. In that runner, you could override setup_test_environment() (docs) or setup_databases() (docs) depending on your needs.

Solution 2:[2]

Finngu's suggestion absolutely works. However - a big reason to split tests is to make them run in individual DB transactions. By sharing the database setup you're somewhat defeating the point.

To make independent tests that share a DB setup, you can just use subTest.

from django.test import TestCase
 

class TestSubTests(ParentClass):
    @classmethod
    def setUpTestData(cls):
        super(TestChild1, cls).setUpTestData()
        cls.parent_attr = "parent value"

    def test_subtests(self):
        
        with self.subTest("The first value"):
            child_attr = "child value 1"
            self.assertEqual(self.parent_attr, "parent value")
            self.assertEqual(self.child_attr, "child value 1")
 
        with self.subTest("The second value"):
            child_attr = "child value 2"
            self.assertEqual(self.parent_attr, "parent value")
            self.assertEqual(self.child_attr, "child value 2")

These subtests run independently - that means the test continues running if one subtest fails. This vs. the custom test runner is a decision that will boil down to details, but subtests are effective and sufficient in most cases. Read more on them i.e.: here.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 finngu
Solution 2 Jura Brazdil