Jean Bouvattier
Jean Bouvattier

Reputation: 365

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.

Upvotes: 3

Views: 1444

Answers (2)

Jura Brazdil
Jura Brazdil

Reputation: 1100

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.

Upvotes: 0

finngu
finngu

Reputation: 527

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.

Upvotes: 2

Related Questions