Voir aussi
Le tutoriel sur les tests, la référence des outils de test et les thématiques avancées des tests.
Ce document est partagé en deux sections principales. Premièrement, nous expliquons comment écrire des tests avec Django. Puis, nous expliquons comment les exécuter.
Les tests unitaires de Django utilisent le module unittest de la bibliothèque Python standard. Ce module définit les tests selon une approche basée sur des classes.
unittest2
Python 2.7 a introduit des modifications majeures de la bibliothèque unittest, en ajoutant certaines fonctionnalités très utiles. Pour garantir que chaque projet Django puisse bénéficier de ces nouvelles fonctionnalités, Django contient une version de unittest2, une copie de la bibliothèque unittest de Python 2.7, rétroprogrammée pour la compatibilité Python 2.6.
Pour accéder à cette bibliothèque, Django fournit l’alias de module django.utils.unittest. Si vous utilisez Python 2.7 ou que vous avez installé unittest2 localement, Django résout l’alias vers ces versions. Dans le cas contraire, Django utilise sa propre version de la bibliothèque unittest2.
Pour utiliser cet alias, écrivez simplement :
from django.utils import unittest
à chaque endroit où vous auriez précédemment écrit :
import unittest
Si vous souhaitez continuer à utiliser l’ancienne bibliothèque unittest, c’est toujours possible, mais vous ne profiterez pas des nouvelles fonctionnalités intéressantes de unittest2.
Voici un exemple de classe héritant de django.test.TestCase, elle-même héritant de unittest.TestCase, et qui lance chaque test dans une transaction pour garantir l’isolation :
from django.test import TestCase
from myapp.models import Animal
class AnimalTestCase(TestCase):
def setUp(self):
Animal.objects.create(name="lion", sound="roar")
Animal.objects.create(name="cat", sound="meow")
def test_animals_can_speak(self):
"""Animals that can speak are correctly identified"""
lion = Animal.objects.get(name="lion")
cat = Animal.objects.get(name="cat")
self.assertEqual(lion.speak(), 'The lion says "roar"')
self.assertEqual(cat.speak(), 'The cat says "meow"')
Lorsque vous lancez vos tests, le comportement par défaut de l’utilitaire de test est de rechercher tous les cas de test (c’est-à-dire les sous-classes de unittest.TestCase) dans tous les fichiers dont le nom commence par test, puis de construire automatiquement une suite de tests et d’exécuter cette suite.
Précédemment, le lanceur de tests par défaut de Django ne découvrait les tests que dans les fichiers tests.py et models.py à l’intérieur d’un paquet Python figurant dans INSTALLED_APPS.
Pour plus de détails au sujet de unittest, consultez la documentation de Python.
Avertissement
Si des tests dépendent de l’accès à une base de données pour créer ou interroger des modèles, les classes de test doivent être des sous-classes de django.test.TestCase plutôt que de
En utilisant unittest.TestCase, on allège les tests en évitant l’étape d’envelopper chaque test dans une transaction et de réinitialiser la base de données. Mais si les tests concernés interagissent avec la base de données, leur comportement variera en fonction de l’ordre dans lequel ils seront lancés par le lanceur de tests. Cela peut conduire certains tests unitaires à réussir lorsqu’il sont exécutés individuellement, mais à échouer lorsqu’ils font partie d’une suite.
Quand les tests ont été écrits, il s’agit de les lancer avec la commande test de l’utilitaire manage.py du projet :
$ ./manage.py test
La découverte des tests se base sur le module de unittest de découverte intégrée des tests. Par défaut, les tests sont recherchés dans tout fichier nommé « test*.py » dans toute l’arborescence à partir du répertoire de travail courant.
Vous pouvez sélectionner certains tests spécifiques à exécuter en indiquant des « noms de tests » à la commande ./manage.py test. Chaque nom de test peut être un chemin Python complet en syntaxe pointée vers un paquet, un module, une sous-classe de TestCase ou une méthode de test. Par exemple :
# Run all the tests in the animals.tests module
$ ./manage.py test animals.tests
# Run all the tests found within the 'animals' package
$ ./manage.py test animals
# Run just one test case
$ ./manage.py test animals.tests.AnimalTestCase
# Run just one test method
$ ./manage.py test animals.tests.AnimalTestCase.test_animals_can_speak
Il est aussi possible d’indiquer un chemin vers un répertoire pour rechercher tous les tests dans l’arborescence de ce répertoire :
$ ./manage.py test animals/
Vous pouvez indiquer un motif de nom de fichier personnalisé en utilisant l’option -p (ou --pattern), dans le cas où vos fichiers de test ne correspondent pas au motif test*.py:
$ ./manage.py test --pattern="tests_*.py"
Précédemment, les noms de test devaient être indiqués sous la forme nom_app, nom_app.TestCase ou nom_app.TestCase.methode_de_test, plutôt que d’utiliser la notation pointée Python habituelle, et les tests n’étaient recherchés que dans les fichiers tests.py ou models.py dans un paquet Python figurant dans INSTALLED_APPS. L’option --pattern et les chemins de fichiers comme noms de tests sont nouveaux dans Django 1.6.
Si vous appuyez sur Ctrl-C pendant que les tests sont en cours d’exécution, le lanceur de tests attend que le test actuellement en cours se termine puis quitte les tests de manière harmonieuse. En quittant les tests de cette façon, le lanceur affiche les détails des échecs de tests, indique combien de tests ont été lancés ainsi que le nombre d’erreurs et d’échecs rencontrés, puis il supprime la ou les bases de données de test comme il le fait d’habitude. Ainsi, Ctrl-C peut être très utile si vous oubliez de passer l’option --failfast, que vous réalisez que certains tests échouent de manière inattendue et que vous souhaitez obtenir les détails de ces échecs sans attendre que toute la suite de tests se termine.
Si vous ne voulez pas attendre que le test actuellement en cours se termine, vous pouvez appuyer une seconde fois sur Ctrl-C et le test se terminera immédiatement, mais de manière brusque. Vous ne verrez aucun détail sur les tests lancés jusqu’à ce point et toute base de données de test créée pour cette exécution ne sera pas supprimée.
Tests avec avertissements actifs
Il est conseillé de lancer les tests en activant les avertissements de Python : python -Wall manage.py test. Le drapeau -Wall indique à Python d’afficher les avertissements d’obsolescence. Django, comme toute autre bibliothèque Python, utilise ces avertissements pour notifier que certaines fonctionnalités sont sur le point de disparaître. Certains avertissements peuvent aussi signaler que certaines zones du code ne sont pas fondamentalement fausses mais qu’elles pourraient bénéficier d’une meilleure implémentation.
Les tests ayant besoin d’une base de données (à savoir les tests avec des modèles) n’utilisent pas la « vraie » base de données (de production). Des bases de données distinctes et vierges sont créées tout exprès pour les tests.
Que les tests passent ou échouent, les bases de données de test sont supprimées dès que tous les tests ont été exécutés.
Par défaut, les noms des bases de données de test sont composés de la valeur du réglage NAME des bases de données définies dans DATABASES préfixés par test_. Avec le moteur de base de données SQLite, les tests utilisent par défaut une base de données en mémoire (c’est-à-dire que la base de données est créée en mémoire sans passer par le système de fichiers). Si vous souhaitez utiliser un autre nom de base de données, renseignez TEST_NAME dans le dictionnaire de réglages DATABASES de la base de données concernée.
Excepté la création d’une base de données séparée, le lanceur de tests utilise les mêmes réglages de base de données de votre fichier de réglages : ENGINE, USER, HOST, etc. La base de données de test est créée par l’utilisateur USER, vous devez donc vérifier que ce compte utilisateur possède les droits nécessaires pour créer une nouvelle base de données dans votre système.
Pour un contrôle plus fin sur le codage de caractères de la base de données de test, il existe une option TEST_CHARSET. Avec MySQL, il est aussi possible d’utiliser l’option TEST_COLLATION pour contrôler la collation utilisée par la base de données de test. Consultez la documentation des réglages pour plus de détails sur ces réglages avancés.
Accès à des données de la base de données de production lors du fonctionnement des tests ?
Si votre code essaie d’accéder à la base de données au moment de la compilation du code, cela se produira avant que la base de données ne soit configurée, avec des résultats potentiellement inattendus. Par exemple, si une requête de base de données est lancée dans du code au niveau module et qu’une base de données réelle existe, des données de production pourraient interférer avec les tests. Il est de toute manière fortement déconseillé de placer des requêtes de base de données exécutées à l’importation du code, il s’agit de réécrire le code pour que cela n’arrive pas.
Voir aussi
Les thématiques avancées des tests avec plusieurs bases de données.
Pour pouvoir garantir que tout code de TestCase commence avec une base de données propre, le lanceur de tests de Django réordonne les tests de la manière suivante :
Toutes les sous-classes de TestCase sont exécutées en premier.
Puis, tous les autres tests unitaires (dont les sous-classes de unittest.TestCase, SimpleTestCase et TransactionTestCase) sont exécutés sans qu’un ordre de tri particulier ne soit garanti.
Puis tous les autres tests (par ex. des « doctests ») qui pourraient modifier la base de données sans la restaurer à son état de départ sont lancés.
Avant Django 1.5, la seule garantie était que les tests TestCase étaient toujours lancés en premier, avant tout autre test.
Note
Le nouvel ordre d’exécution des tests pourrait révéler des dépendances non prévues dans l’ordre des cas de test. C’est le cas avec les « doctests » qui comptaient sur un état de base de données suite à un test TransactionTestCase donné ; ces tests doivent être mis à jour pour pouvoir fonctionner de manière indépendante.
Tous les tests de Django sont exécutés avec DEBUG=False, quel que soit la valeur du réglage DEBUG dans votre fichier de configuration. Cela permet de garantir que le résultat observé du code correspond à ce qui sera effectivement produit en production.
Les caches ne sont pas effacés après chaque test et le lancement de « manage.py test fooapp » peut insérer des données de test dans le cache d’un système en ligne si vous lancez les tests en production car au contraire de ce qui est fait avec les bases de données, les tests n’utilisent pas un « cache de test ». Ce comportement pourrait changer à l’avenir.
Lorsque vous lancez des tests, un certain nombre de messages apparaissent alors que le lanceur de test se prépare. Vous pouvez contrôler le niveau de détail de ces messages avec l’option verbosity en ligne de commande :
Creating test database...
Creating table myapp_animal
Creating table myapp_mineral
Loading 'initial_data' fixtures...
No fixtures found.
Le lanceur de tests vous informe qu’il crée une base de données de test, comme indiqué dans la section précédente.
Après la création de la base de données de test, Django se met à exécuter les tests proprement dits. Si tout se passe bien, vous verrez quelque chose comme ceci :
----------------------------------------------------------------------
Ran 22 tests in 0.221s
OK
Cependant, si des tests ont échoué, vous verrez des détails complets au sujet des tests ayant échoué :
======================================================================
FAIL: test_was_published_recently_with_future_poll (polls.tests.PollMethodTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/dev/mysite/polls/tests.py", line 16, in test_was_published_recently_with_future_poll
self.assertEqual(future_poll.was_published_recently(), False)
AssertionError: True != False
----------------------------------------------------------------------
Ran 1 test in 0.003s
FAILED (failures=1)
Il n’est pas dans l’optique de ce document d’expliquer en détails cet affichage d’erreur, mais c’est relativement intuitif. Vous pouvez consulter la documentation Python de la bibliothèque unittest pour plus de détails.
Notez que le code de retour du script de lancement des tests est 1 quel que soit le nombre de tests en échec ou en erreur. Si tous les tests passent, le code de retour est 0. Cette fonctionnalité est utile si vous lancez le script de lancement des tests à partir d’un script shell et que vous avez besoin de tester le résultat (succès ou échec) à ce niveau.
Dans les récentes versions de Django, le hachage par défaut du mot de passe est délibérément assez lent. Si vos tests font l’authentification de beaucoup d’utilisateurs, il est conseillé de créer un fichier de réglages propre aux tests et de définir le réglage PASSWORD_HASHERS à un algorithme de hachage plus rapide :
PASSWORD_HASHERS = (
'django.contrib.auth.hashers.MD5PasswordHasher',
)
N’oubliez pas d’inclure aussi dans PASSWORD_HASHERS tout algorithme de hachage utilisé dans les instantanés, le cas échéant.
Jan 13, 2016