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 effectuer de petits tests rapides sans aucune dépendance sur le datastore.
Ce document fournit des informations sur la configuration d'un framework de test, puis explique comment écrire des tests d'unités sur plusieurs services App Engine locaux.
Configurer un framework de test
Même si les utilitaires de test du SDK ne sont liés à aucun framework en particulier, ce guide utilise JUnit pour les exemples. Ainsi, vous disposez d'éléments de travail concrets et complets. Avant de commencer à écrire des tests, vous devez ajouter le fichier JAR JUnit 4 approprié au paramètre "classpath" de vos tests. Une fois ce fichier ajouté, vous pouvez commencer à écrire un test JUnit simple.
Si vous exécutez Eclipse, sélectionnez le fichier source du test à exécuter. Sélectionnez le menu Run (Exécuter) > Run As (Exécuter en tant que) > JUnit Test (Test JUnit). Les résultats du test apparaissent dans la fenêtre de la console.
Présentation des utilitaires de test Java 8
MyFirstTest
illustre la configuration de test la plus simple qui soit. Pour les tests qui ne dépendent pas des API App Engine ou de la mise en œuvre de services locaux, il est possible que vous n'ayez besoin de rien de plus. Toutefois, si vos tests ou le code en cours de test comportent ce type de dépendance, ajoutez les fichiers JAR suivants au paramètre "classpath" de votre test :
${SDK_ROOT}/lib/impl/appengine-api.jar
${SDK_ROOT}/lib/impl/appengine-api-stubs.jar
${SDK_ROOT}/lib/appengine-tools-api.jar
Ces fichiers permettent de mettre les API d'exécution et les mises en œuvre locales de ces API à disposition de vos tests.
Les services App Engine ont besoin que leur environnement d'exécution leur fournisse un certain nombre d'éléments, et la configuration de ces éléments suppose une certaine quantité de code récurrent. Au lieu de les configurer vous-même, vous pouvez employer les utilitaires du package com.google.appengine.tools.development.testing
. Pour pouvoir utiliser ce package, ajoutez le fichier JAR suivant au paramètre "classpath" du test :
${SDK_ROOT}/lib/testing/appengine-testing.jar
Prenez une minute pour parcourir le fichier javadoc du package com.google.appengine.tools.development.testing
. La classe la plus importante de ce package est LocalServiceTestHelper. Celle-ci gère l'ensemble de la configuration nécessaire de l'environnement, et fournit un point de configuration de niveau supérieur pour tous les services locaux auxquels vous pourriez souhaiter accéder lors de vos tests.
Pour écrire un test qui accède à un service local donné, procédez comme suit :
- Créez une instance de classe
LocalServiceTestHelper
avec une mise en œuvre deLocalServiceTestConfig
pour ce service local spécifique. - Appelez
setUp()
dans votre instanceLocalServiceTestHelper
avant chaque test ettearDown()
après chaque test.
Écrire des tests pour le datastore et Memcache
L'exemple suivant permet de tester l'utilisation du service du datastore.
Dans cet exemple, LocalServiceTestHelper
configure et nettoie les parties de l'environnement d'exécution qui sont communes à tous les services locaux et LocalDatastoreServiceTestConfig
configure et nettoie les parties de l'environnement d'exécution qui sont propres au service Datastore local. Si vous lisez le fichier javadoc, vous apprendrez que cela implique de configurer le service de datastore local pour conserver toutes les données en mémoire (plutôt que de vider le disque à intervalles réguliers) et d'effacer toutes les données en mémoire à la fin de chaque test. Il s'agit simplement du comportement par défaut pour un test de datastore. Si ce comportement ne correspond pas à vos attentes, vous pouvez le modifier.
Modifier l'exemple pour accéder au cache mémoire au lieu du datastore
Pour créer un test qui accède au service Memcache local, vous pouvez utiliser le code proposé ci-dessus, avec quelques modifications.
Au lieu d'importer des classes relatives au datastore, importez celles concernant le cache mémoire. Vous devez quand même importer LocalServiceTestHelper
.
Modifiez le nom de la classe que vous êtes en train de créer, ainsi que l'instance de LocalServiceTestHelper
, de sorte qu'ils soient spécifiques au cache mémoire.
Enfin, modifiez le mode d'exécution du test afin qu'il soit pertinent pour le cache mémoire.
Comme dans l'exemple du datastore, LocalServiceTestHelper
et la configuration LocalServiceTestConfig
spécifique au service (dans ce cas LocalMemcacheServiceTestConfig
) gèrent l'environnement d'exécution.
É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. LocalDatastoreServiceTestConfig
propose des options qui facilitent cette écriture :
En définissant le pourcentage de tâches non appliquées sur 100, nous indiquons au datastore local 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 à afficher les modifications. Cette quantité de cohérence à terme n'est évidemment pas représentative de celle que votre application affichera lors de son exécution dans l'environnement de production. Toutefois, à des fins de test, il peut s'avérer utile de pouvoir configurer le comportement du datastore local de cette manière à chaque fois.
Si vous souhaitez avoir un contrôle plus précis des transactions qui échouent, vous pouvez enregistrer votre propre règle HighRepJobPolicy
:
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 file d'attente des tâches
Les tests qui utilisent la file d'attente des tâches locale sont un peu plus complexes. En effet, contrairement au datastore et au cache mémoire, l'API ne propose aucune fonction permettant d'examiner l'état du service. Nous devons alors accéder à la file d'attente des tâches locale pour vérifier qu'une tâche a été planifiée avec les paramètres attendus. Pour ce faire, nous avons besoin de com.google.appengine.api.taskqueue.dev.LocalTaskQueue
.
Remarquez la façon dont nous demandons à LocalTaskqueueTestConfig
de gérer l'instance de service local. Nous examinons ensuite le service local lui-même pour vérifier que la tâche a été planifiée comme prévu. Toutes les mises en œuvre LocalServiceTestConfig
proposent une méthode similaire. Vous n'en aurez pas toujours l'utilité, mais elle s'avérera indispensable lorsque vous en aurez besoin.
Définir le fichier de configuration queue.xml
Les bibliothèques de test de la file d'attente des tâches permettent le nombre de configurations queue.xml
souhaité, à définir sur une base "LocalServiceTestHelper" via la méthode LocalTaskQueueTestConfig.setQueueXmlPath
. Actuellement, tout paramètre de limite de débit dans la file d'attente est ignoré par le serveur local de développement.
Il n'est pas possible d'exécuter plusieurs tâches simultanément.
Par exemple, un projet peut avoir besoin de tester le fichier queue.xml
qui sera importé et utilisé par l'application App Engine. En supposant que le fichier queue.xml
se trouve à l'emplacement standard, l'exemple de code ci-dessus peut être modifié comme suit pour accorder au test l'accès aux files d'attente spécifiées dans le fichier src/main/webapp/WEB-INF/queue.xml
:
Modifiez le chemin d'accès au fichier queue.xml
pour l'adapter à la structure du fichier de votre projet.
Utilisez la méthode QueueFactory.getQueue
pour accéder aux files d'attente par nom :
Écrire des tests de tâches différées
Si le code de votre application utilise des tâches différées, les utilitaires de test Java facilitent l'écriture d'un test d'intégration qui vérifie les résultats de ces tâches.
Comme dans notre premier exemple de file d'attente de tâches locale, nous utilisons LocalTaskqueueTestConfig
, mais cette fois, nous l'initialisons avec des arguments supplémentaires qui nous permettent de vérifier facilement non seulement que la tâche a été planifiée, mais également qu'elle a été exécutée. Nous appelons setDisableAutoTaskExecution(false)
pour indiquer à la file d'attente de tâches locale d'exécuter automatiquement les tâches. Nous appelons setCallbackClass(LocalTaskQueueTestConfig.DeferredTaskCallback.class)
pour indiquer à la file d'attente de tâches locale d'utiliser une fonction de rappel qui comprenne comment exécuter les tâches différées. Enfin, nous appelons setTaskExecutionLatch(latch)
pour indiquer à la file d'attente de tâches locale de réduire le verrou après chaque exécution de tâche. Cette configuration nous permet d'écrire un test dans lequel nous mettons une tâche différée en file d'attente, attendons qu'elle soit exécutée, puis vérifions qu'elle a eu le comportement attendu lors de son exécution.
Écrire des tests des fonctionnalités du service local
Le test des fonctionnalités implique de modifier le statut de certains services, tels que le datastore, le blobstore, le cache mémoire, entre autres, et d'exécuter votre application sur ce service pour déterminer si votre application répond comme prévu dans des conditions différentes. L'état des fonctionnalités peut être modifié à l'aide de la classe LocalCapabilitiesServiceTestConfig.
L'extrait de code suivant permet de modifier l'état des fonctionnalités du service du datastore pour le désactiver, puis de réaliser un test sur le service du datastore. Vous pouvez substituer d'autres services au datastore si nécessaire.
L'exemple de test crée d'abord un objet Capability
initialisé sur le datastore, puis crée un objet CapabilityStatus
défini sur DISABLED (DÉSACTIVÉ). La classe LocalCapabilitiesServiceTestConfig
est créée à l'aide de la fonctionnalité et de l'état définis par les objets Capability
et CapabilityStatus
que vous venez de créer.
La classe LocalServiceHelper
est ensuite créée à l'aide de l'objet LocalCapabilitiesServiceTestConfig
. Une fois le test configuré, DatastoreService
est créé, puis une requête lui est envoyée pour déterminer si le test génère les résultats attendus, dans le cas présent, une exception CapabilityDisabledException
.
Écrire des tests pour d'autres services
Des utilitaires de test sont disponibles pour le blobstore et d'autres services App Engine. Pour obtenir la liste de tous les services disposant de mises en œuvre locales pour les tests, consultez la documentation sur LocalServiceTestConfig
.
Écrire des tests avec des attentes en matière d'authentification
Cet exemple illustre comment écrire des tests qui vérifient la logique utilisée par UserService pour déterminer si un utilisateur est connecté ou dispose des droits d'administrateur. Notez que tout utilisateur dispose des droits d'administrateur, dès lors qu'il possède le rôle de base "Lecteur", "Éditeur" ou "Propriétaire", ou le rôle prédéfini "Administrateur App Engine".
Dans cet exemple, nous configurons LocalServiceTestHelper
avec LocalUserServiceTestConfig
. Nous pouvons donc utiliser UserService
dans notre test, mais nous configurons également des données d'environnement relatives à l'authentification dans LocalServiceTestHelper
.
Dans cet exemple, nous configurons LocalServiceTestHelper
avec LocalUserServiceTestConfig
afin de pouvoir utiliser OAuthService
.