Les tests unitaires permettent de vérifier la qualité de votre code après l'avoir écrit, mais également d'améliorer progressivement votre processus de développement. Au lieu d'écrire les tests une fois que vous avez fini de développer votre application, écrivez-les en même temps. Vous pouvez ainsi concevoir de petites unités de code gérables et réutilisables. Il est également plus simple pour vous de tester complètement et rapidement votre code.
Lorsque vous réalisez un test unitaire local, celui-ci s'exécute dans votre propre environnement de développement sans impliquer de composants distants. App Engine propose des utilitaires de test qui s'appuient sur des mises en œuvre locales du datastore et d'autres services App Engine. De cette façon, vous pouvez exécuter le code en local, sans le déployer sur App Engine, grâce aux simulations de service.
Une simulation de service est une méthode qui simule le comportement du service. Par exemple, la simulation de service du datastore indiquée dans la section Écrire des tests pour le datastore et le cache mémoire vous permet de tester le code de votre datastore, sans effectuer de requête auprès du véritable datastore. Toute entité stockée lors d'un test unitaire du datastore est conservée en mémoire (pas dans le datastore), puis en est supprimée après le test. Vous pouvez donc effectuer de petits tests rapides, sans aucune dépendance sur le datastore.
Ce document décrit la procédure d'écriture de tests unitaires pour plusieurs services App Engine locaux et donne des informations sur la configuration d'un framework de test.
Présentation des utilitaires de test Python 2
Un module App Engine Python nommé testbed
assure la disponibilité des simulations de service dans le cadre des tests unitaires.
Voici les services pour lesquels des simulations de service sont disponibles :
- Identité d'application
init_app_identity_stub
- Blobstore (utiliser
init_blobstore_stub
) - Capacité (utiliser
init_capability_stub
) - Datastore (utiliser
init_datastore_v3_stub
) - Fichiers (utiliser
init_files_stub
) - Images (uniquement pour dev_appserver ; utiliser
init_images_stub
) - LogService (utiliser
init_logservice_stub
) - Messagerie (utiliser
init_mail_stub
) - Memcache (utiliser
init_memcache_stub
) - File d'attente de tâches (utiliser
init_taskqueue_stub
) - Récupération d'URL (utiliser
init_urlfetch_stub
) - Service Users (utiliser
init_user_stub
)
Pour initialiser toutes les simulations en même temps, vous pouvez utiliser init_all_stubs
.
Écrire des tests pour le datastore et Memcache
Cette section indique comment écrire du code permettant de tester l'utilisation des services datastore et Memcache.
Assurez-vous que votre lanceur de test dispose des bibliothèques appropriées sur le chemin de chargement Python, y compris les bibliothèques App Engine, yaml
(inclus dans le SDK App Engine), la racine de l'application et toute autre modification du chemin de la bibliothèque attendue par le code d'application (comme un répertoire ./lib
local, si vous en avez un). Exemple :
import sys
sys.path.insert(1, 'google-cloud-sdk/platform/google_appengine')
sys.path.insert(1, 'google-cloud-sdk/platform/google_appengine/lib/yaml/lib')
sys.path.insert(1, 'myapp/lib')
Importez le module Python unittest
et les modules App Engine pertinents pour les services testés, dans ce cas memcache
et ndb
, qui utilisent à la fois Datastore et Memcache. Importez également le module testbed
.
Créez ensuite une classe TestModel
. Dans cet exemple, une fonction s'assure qu'une entité est stockée dans Memcache. Si elle ne trouve aucune entité, elle en recherche une dans le datastore. Cela peut souvent être redondant dans la vie réelle, car ndb
utilise Memcache lui-même en arrière-plan, mais cela reste un modèle acceptable pour un test.
Ensuite, créez un scénario de test. Quels que soient les services testés, le scénario de test doit créer une instance Testbed
et l'activer. Le scénario de test doit également initialiser les simulations de service pertinentes en utilisant, dans le cas présent, init_datastore_v3_stub
et init_memcache_stub
. Les méthodes d'initialisation d'autres simulations de service App Engine sont répertoriées dans la section Présentation des utilitaires de test Python.
La méthode init_datastore_v3_stub()
sans argument utilise un datastore en mémoire initialement vide. Si vous voulez tester une entité existante dans le datastore, ajoutez son chemin d'accès en tant qu'argument à init_datastore_v3_stub()
.
En plus de setUp()
, ajoutez une méthode tearDown()
qui désactive le module testbed. Ceci permet de rétablir les simulations d'origine, de sorte que les tests n'interfèrent pas entre eux.
Puis mettez en œuvre les tests.
Désormais, vous pouvez utiliser TestModel
pour écrire des tests qui font appel aux simulations de service Datastore ou Memcache au lieu de faire appel aux services réels.
Par exemple, la méthode illustrée ci-dessous crée deux entités : la première entité utilise la valeur par défaut de l'attribut number
(42) et la seconde utilise une valeur non définie par défaut pour number
(17). La méthode crée ensuite une requête pour les entités TestModel
, mais seulement pour celles dont la valeur par défaut est number
.
Une fois toutes les entités correspondantes récupérées, la méthode vérifie qu'une entité a été trouvée et que la valeur d'attribut number
de cette entité est la valeur par défaut.
Autre exemple : la méthode suivante crée une entité et la récupère à l'aide de la fonction GetEntityViaMemcache()
créée précédemment. La méthode vérifie ensuite qu'une entité a été renvoyée et que sa valeur number
est identique à celle de l'entité précédemment créée.
Enfin, appelez unittest.main()
.
Pour exécuter les tests, consultez la section Exécuter des tests.
Écrire des tests pour Cloud Datastore
Si votre application utilise Cloud Datastore, vous souhaitez peut-être écrire des tests permettant de vérifier le comportement de votre application en cas de cohérence à terme.
db.testbed
propose des options qui simplifient cette tâche :
La classe PseudoRandomHRConsistencyPolicy
vous permet de contrôler la probabilité pour qu'une écriture soit appliquée avant chaque requête globale (non ascendante). En définissant la probabilité sur 0 %, nous indiquons à la simulation du datastore de fonctionner avec la quantité maximale de cohérence à terme. Une cohérence à terme maximale signifie que les écritures seront validées, mais que leur application échouera systématiquement, si bien que les requêtes globales (non ascendantes) ne parviendront jamais à voir les modifications. Cela n'est évidemment pas représentatif de la quantité de cohérence à terme que votre application verra lors de son exécution dans l'environnement de production mais, à des fins de test, pouvoir configurer le datastore local pour qu'il se comporte à chaque fois de cette manière s'avère particulièrement utile. Si vous utilisez une probabilité non nulle, PseudoRandomHRConsistencyPolicy
établit une séquence déterministe de décisions de cohérence de sorte que les résultats des tests soient cohérents :
Les API de test permettent de vérifier que votre application se comporte correctement face à la cohérence à terme. Toutefois, n'oubliez pas que le modèle local de cohérence de lecture avec réplication avancée est une approximation du modèle dans l'environnement de production, et non une réplique exacte. Dans l'environnement local, l'exécution d'une opération get()
d'une entité Entity
appartenant à un groupe d'entités avec une écriture non appliquée, rendra toujours les résultats de l'écriture non appliquée visibles pour les requêtes globales suivantes, ce qui n'est pas le cas dans l'environnement de production.
Écrire des tests de messagerie
Vous pouvez utiliser la simulation de service de messagerie pour tester le service de messagerie. Comme pour les autres services compatibles avec testbed, vous devez d'abord initialiser la simulation, puis appeler le code utilisant l'API de messagerie et enfin vérifier si les bons messages ont été envoyés.
Écrire des tests de file d'attente des tâches
Vous pouvez utiliser la simulation de file d'attente de tâches pour écrire des tests qui utilisent le service taskqueue. Comme pour les autres services compatibles avec testbed, vous devez d'abord initialiser la simulation, puis appeler le code qui utilise l'API Task Queue et enfin vérifier si les tâches ont été correctement ajoutées à la file d'attente.
Définir le fichier de configuration queue.yaml
Si vous souhaitez exécuter des tests sur du code qui interagit avec une file d'attente autre que celle par défaut, vous devez créer et spécifier un fichier queue.yaml
que votre application utilisera.
Vous trouverez un exemple de fichier queue.yaml
ci-dessous.
Pour plus d'informations sur les options queue.yaml disponibles, consultez la page Configuration de la file d'attente de tâches.
L'emplacement du fichier queue.yaml
est spécifié lors de l'initialisation de la simulation :
self.testbed.init_taskqueue_stub(root_path='.')
Dans l'exemple, queue.yaml
se trouve dans le même répertoire que les tests. S'il se trouve dans un autre dossier, ce chemin d'accès doit être spécifié dans root_path
.
Filtrer des tâches
La méthode get_filtered_tasks
de la simulation de file d'attente de tâches vous permet de filtrer les tâches en file d'attente.
Cela facilite l'écriture des tests dont l'objectif est de vérifier le code qui place plusieurs tâches en file d'attente.
Écrire des tests de tâches différées
Si le code d'application utilise la bibliothèque différée, vous pouvez avoir recours à la simulation de file d'attente de tâches avec deferred
pour vérifier que les fonctions différées sont mises en file d'attente et exécutées correctement.
Modifier les variables d'environnement par défaut
Les services App Engine dépendent souvent de variables d'environnement. La méthode activate()
de la classe testbed.Testbed
utilise des valeurs par défaut pour celles-ci, mais vous pouvez définir des valeurs personnalisées en fonction de vos besoins de test avec la méthode setup_env
de la classe testbed.Testbed
.
Par exemple, prenons le cas d'un test qui stocke plusieurs entités dans un datastore, celles-ci étant toutes liées au même ID d'application. Maintenant vous voulez réexécuter les mêmes tests, mais en utilisant un ID d'application différent de celui lié aux entités stockées. Pour ce faire, transmettez la nouvelle valeur dans self.setup_env()
en tant que app_id
.
Exemple :
Simuler la connexion
La méthode setup_env
est également fréquemment utilisée pour simuler un utilisateur connecté, avec ou sans privilèges d'administrateur, pour vérifier si vos gestionnaires fonctionnent correctement dans chaque cas.
Désormais, vos méthodes de test peuvent, par exemple, appeler self.loginUser('', '')
pour simuler l'absence d'utilisateur connecté, self.loginUser('test@example.com', '123')
pour simuler la connexion d'un utilisateur non administrateur, self.loginUser('test@example.com',
'123', is_admin=True)
pour simuler la connexion d'un utilisateur administrateur.
Configurer un framework de test
Les utilitaires de test du SDK ne sont associés à aucun framework donné. Vous pouvez exécuter vos tests unitaires avec n'importe quel lanceur de test App Engine disponible, par exemple nose-gae ou ferrisnose. Vous pouvez également écrire un lanceur de test simple ou utiliser celui illustré ci-dessous.
Les scripts suivants utilisent le module Python unittest.
Vous pouvez donner le nom que vous voulez au script. Lorsque vous l'exécutez, indiquez le chemin d'accès à votre installation de Google Cloud CLI ou du SDK Google App Engine, ainsi que le chemin d'accès à vos modules de test. Le script va détecter tous les tests dans le chemin fourni et va imprimer les résultats dans le flux d'erreur standard. Les fichiers de test suivent la convention voulant que leur nom contienne le préfixe test
.
Exécuter les tests
Vous pouvez exécuter ces tests simplement en exécutant le script runner.py
, qui est décrit en détail dans la section Configurer un framework de test :
python runner.py <path-to-appengine-or-gcloud-SDK> .