Makina Blog

Le blog Makina-corpus

Optimiser ses tests unitaires Django avec setUpTestData


Découvrez comment gagner en efficacité sur les tests unitaires et sa méthode d'initialisation des tests : setUpTestData.

Django intègre une méthode permettant de créer les données une seul fois pour tout une série de tests : setUpTestData. Nous avons ainsi pu passer de 180 secondes à 90 secondes en mettant nos tests à jours sur un de nos projet. Nous allons voir ici comment utiliser et mettre en place cette fonctionnalité sur de nouveaux tests ou même des tests déjà existants.

Partons du modèle suivant :

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

Et des tests suivants :

class PersonTest(TestCase):
    def setUp(self):
        self.alice = Person.objects.create(first_name='Alice', last_name='Smith')
        self.bob = Person.objects.create(first_name='Bob', last_name='Smith')

    def test_alice_first_name(self):
        self.assertEqual(self.alice.first_name, 'Alice')

    def test_bob_first_name(self):
        self.assertEqual(self.bob.first_name, 'Bob')

    def test_bob_first_name_modified(self):
       self.bob.first_name = 'Jack'
        self.bob.save()
        self.assertEqual(self.bob.first_name, 'Jack')

Nous avons donc deux requêtes exécutées avant chaque test. Ce qui nous fais un total de six requêtes effectuées juste pour créer les donnés nécessaires aux tests. setUpTestData utilise les fonctionnalités des transactions des bases de données et nous permettrais de n'exécuter les requêtes qu'une seul fois pour tout les tests. setUpTestData est une méthode de classe et son utilisation est donc un peu différente de setUp :

class PersonTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.alice = Person.objects.create(first_name='Alice', last_name='Smith')
        cls.bob = Person.objects.create(first_name='Bob', last_name='Smith')

    def test_alice_first_name(self):
        self.assertEqual(self.alice.first_name, 'Alice')

    def test_bob_first_name(self):
        self.assertEqual(self.bob.first_name, 'Bob')

    def test_bob_first_name_modified(self):
       self.bob.first_name = 'Jack'
        self.bob.save()
        self.assertEqual(self.bob.first_name, 'Jack')

Les requêtes de création ne seront donc désormais exécutées qu'une seul fois. Mais il reste un autre problème si vous utilisez une version de Django plus ancienne que la 3.2. Si nous faisons tourner les tests tels quels, nous nous rendrions compte que self.bob reste modifié après le test test_bob_first_name_modified. Ceci est du au fait que l'objet self.bob est modifié en mémoire et n'est pas restauré après le test (ce qui est fait automatiquement à partir de Django 3.2). Nous pouvons faire en sorte de recharger les objet avant chaque test à l'aide de setUp et d'une autre fonctionnalité de Django : refresh_from_db.

class PersonTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.alice = Person.objects.create(first_name='Alice', last_name='Smith')
        cls.bob = Person.objects.create(first_name='Bob', last_name='Smith')

    def setUp(self):
        self.alice.refresh_from_db()
        self.bob.refresh_from_db()

    def test_alice_first_name(self):
        self.assertEqual(self.alice.first_name, 'Alice')

    def test_bob_first_name(self):
        self.assertEqual(self.bob.first_name, 'Bob')

    def test_bob_first_name_modified(self):
        self.bob.first_name = 'Jack'
        self.bob.save()
        self.assertEqual(self.bob.first_name, 'Jack')

Même si cela fait deux requêtes en plus à chaque test, ce sont des opérations de lecture et non d'écriture, et donc beaucoup plus rapide.

Il est finalement très simple de mettre en place cette nouvelle fonctionnalité, que ce soit pour de nouveaux tests ou d'anciens, permettant un gain de temps assez important.

Actualités en lien

Image
Encart PyConFr 2024
21/10/2024

Makina Corpus est spon­sor de la PyConFR 2024

Le soutien de Makina Corpus à la PyConFR 2024, qui se tient du 31 octobre au 3 novembre 2024 à Stras­bourg, reflète ses valeurs de partage et d’in­no­va­tion, et son enga­­ge­­ment envers la commu­nauté dyna­­mique et ouverte de Python.

Voir l'article
Image
Webinaire découverte de Canari
10/04/2024

Revoir les webi­naires : décou­verte de l’ou­til CANARI-France

L’ap­pli­ca­tion CANARI-France est destiné aux acteurs agri­coles afin de calcu­ler des indi­ca­teurs agro-clima­tiques à partir de projec­tions clima­tiques. Décou­vrer en le replay des 4 webi­naires orga­ni­sés par Sola­gro et l’ADEME.

Voir l'article
Image
Django PostgreSQL
07/11/2023

Utiliser des fonctions PostgreSQL dans des contraintes Django

Cet article vous présente comment utiliser les fonctions et les check constraints PostgreSQL en tant que contrainte sur vos modèles Django.

Voir l'article

Inscription à la newsletter

Nous vous avons convaincus