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.