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
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
Image
Encart Django
06/11/2023

Comment migrer vers une version récente de Django ?

Que ce soit pour avoir les dernières fonctionnalités ou les correctifs de sécurité, rester sur une version récente de Django est important pour la pérennité de son projet.

Voir l'article
Image
Python
26/07/2023

La formation Python éligible au CPF est enfin arrivée

Makina Corpus propose un nouvelle formation Python éligible au CPF. Grâce à cette certification, cette formation peut être entièrement financée par votre compte Compte Personnel de Formation.

Voir l'article

Inscription à la newsletter

Nous vous avons convaincus