Le blog: Web, Python, Django, Javascript ...

Tests unitaires avec Django

Django est une solution très aboutie pour le développement web. Il accélère le développement en proposant des fonctionnalités prêtes à l'emploi comme par exemple le site d'administration automatiquement généré depuis le modèle de données. Il inclut aussi des outils très utiles pour faciliter le développement comme un serveur web local. Je vais vous présenter rapidement ici une autre fonctionnalité très utile pour le développement: le module de test unitaire.

A la création d'une application, Django génère un fichier test.py qui contient 2 tests d'exemple avec les 2 formats supportés : doctest et pyunit. Je préfèrere personnellement  le format pyunit et je me limiterai à celui-ci dans cet article.

from django.test import TestCase

class SimpleTest(TestCase):
    def test_basic_addition(self):
        """
        Tests that 1 + 1 always equals 2.
        """
        self.failUnlessEqual(1 + 1, 2)

__test__ = {"doctest": """
Another way to test that 1 + 1 is equal to 2.

>>> 1 + 1 == 2
True
"""}

Un test est tout simplement une methode d'une classe dérivée de django.test.TestCase et commençant par 'test'. En lançant une commande python manage.py test monapp, Django va charger les test cases et l'ensemble des tests. L'exécution de chacun est encadrée par une initialisation 'setUp' et un retour à l'état de base 'tearDown'. S'il s'effectue sans exception, le test est considéré comme valide. Mais la classe TestCase fournit un ensemble de méthodes pour des vérifications tout au long de l'exécution.

Ainsi chaque test est exécuté dans un contexte propre afin d'éviter les effets de bord. Django réinitialise la base de données évitant ainsi que le résultat d'un test soit impacté par l'exécution d'un autre.

C'est une des grandes forces, je trouve, que de gérer automatiquement toute cette mécanique. Le testeur peut se concentrer sur la logique du test. Autre avantage, on peut travailler en même temps et de manière complètement transparente sur une base de test et une base de développement. Un gain de temps appréciable au quotidien.

Autre fonctionnalité très importante, le client HTTP qui permet de récupérer le contenu d'une page web et de vérifier son statut. Ce client joue le rôle du navigateur dans une application Internet. Il permet aussi de simuler la connexion d'un utilisateur via les méthodes login et logout.

Voici un exemple très simple pour illustrer ce propos.

class PageTest(TestCase):
    fixtures = ['user-unittest.json']
    
    def _create_page(self, url, title, content):
        """cree une page sur le site"""
       
        #creation d'une page
        page = models.CmsPage()
        page.url = url
        page.title = title
        page.content = content
        page.save()
        return page

    def _check_page(self, page, err_code=0):
        """verification de la bonne creation de la page"""
       
        #on recupere la page
        response = self.client.get(page.url)
        
        #on verifie le code d'erreur
        if err_code:
            self.assertEqual(response.status_code, err_code)
       
        #on verifie le contenu de la page
        if response.status_code == 404:
            self.assertContains(response, page.title)
            self.assertContains(response, page.content)

    def test_create_page(self):
        """Test qu'une page peut-etre cree correctement"""
        page = self._create_page("/", "Titre5487", "Contenu5555")
        self._check_page(page)

Dans cet exemple simple, j'ai préféré séparer clairement la réalisation des actions et la vérification des résultats (Oracle de test). On factorise ainsi pour l'ensemble des tests et on peut faire évoluer le jeu de test plus facilement par exemple en ajoutant de nouvelles vérifications sur la page.

Vous avez peut-être noté l'utilisation de assertContains qui vérifie que la réponse HTTP contient bien une chaîne de caractères. Il existe aussi une méthode assertNotContains et aussi des méthodes pour valider les templates Django utilisés pour construire une page.

Autre aspect à noter, la possibilité d'inclure des fixtures, fichier json contenant des données à insérer dans la base avant chaque test comme par exemple la création de comptes utilisateurs.

Le framework de test est bien un outil pour les tests unitaires et il se limite à cet usage. Pour les tests fonctionnels, fait en phase d'intégration ou de validation système, il faut utiliser selenium qui lui permet de piloter réellement un navigateur et de vérifier le fonctionnement réel de la page web dont la structure est plus complexe (plusieurs requêtes HTTP, utilisation de javascript, d'ajax ...)

Dans tous les cas, Django prouve que c'est une framework qui a pensé de manière pragmatique à tous les aspects d'un projet web en apportant un soin particulier aux tests unitaires.


Nom: Email: URL: Commentaire: Si vous saisissez quelque chose dans ce champ, votre commentaire sera considéré comme étant indésirable: Captcha: captcha

Luc JEAN

09.65.20.15.70

ljean@apidev.fr

Luc JEAN

Suivez les nouveautés

Wikio RSS  RSS Blog Python Django selenium Rss commentaires
Paperblog : Les meilleurs actualités issues des blogs Follow luc_apidev on Twitter