Automatiser les tests de l'interface utilisateur

Tester les interactions utilisateur permet de s'assurer que les utilisateurs ne rencontrent pas de résultats inattendus ou n'ont pas une mauvaise expérience lorsqu'ils interagissent avec votre application. Prenez l'habitude de créer des tests d'interface utilisateur (UI) si vous devez vérifier que l'UI de votre application fonctionne correctement.

Une approche des tests de l'interface utilisateur consiste simplement à demander à un testeur humain d'effectuer un ensemble d'opérations utilisateur sur l'application cible et de vérifier qu'elle se comporte correctement. Cependant, cette approche manuelle peut s'avérer chronophage et source d'erreurs. Une approche plus efficace consiste à écrire vos tests d'interface utilisateur de sorte que les actions utilisateur soient effectuées de manière automatisée. L'approche automatisée vous permet d'exécuter vos tests de manière rapide et fiable de manière reproductible.

Les tests d'interface utilisateur lancent une application (ou une partie de celle-ci), puis simulent les interactions utilisateur. Enfin, ils vérifient que l'application a réagi de manière appropriée. Ces tests d'intégration peuvent aller de la vérification du comportement d'un petit composant à un test de navigation à grande échelle qui balaie l'ensemble d'un parcours utilisateur. Ils sont utiles pour vérifier les régressions et pour vérifier la compatibilité avec différents niveaux d'API et appareils physiques.

Tests d'interface utilisateur d'instrumentation dans Android Studio

Pour exécuter des tests d'interface utilisateur d'instrumentation à l'aide d'Android Studio, implémentez votre code de test dans un dossier de test Android distinct : src/androidTest/java. Le plug-in Android pour Gradle crée une application de test basée sur votre code de test, puis charge l'application de test sur le même appareil que l'application cible. Dans votre code de test, vous pouvez utiliser des frameworks de test d'interface utilisateur pour simuler des interactions utilisateur sur l'application cible afin d'effectuer des tâches de test couvrant des scénarios d'utilisation spécifiques.

Frameworks Jetpack

Jetpack comprend plusieurs frameworks qui fournissent des API pour écrire des tests d'interface utilisateur:

  • Le framework de test Espresso (Android 4.0.1, niveau d'API 14 ou supérieur) fournit des API permettant d'écrire des tests d'interface utilisateur pour simuler des interactions utilisateur avec des vues dans une application cible unique. L'un des principaux avantages d'Espresso est qu'il fournit une synchronisation automatique des actions de test avec l'interface utilisateur de l'application que vous testez. Espresso détecte lorsque le thread principal est inactif. Il peut donc exécuter vos commandes de test au moment approprié, ce qui améliore la fiabilité de vos tests.
  • Jetpack Compose (Android 5.0, niveau d'API 21 ou supérieur) fournit un ensemble d'API de test pour lancer les écrans et les composants Compose et interagir avec eux. Les interactions avec les éléments Compose sont synchronisées avec les tests et ont un contrôle total sur le temps, les animations et les recompositions.
  • UI Automator (Android 4.3, niveau d'API 18 ou supérieur) est un framework de test d'UI adapté aux tests fonctionnels interapplis de l'interface utilisateur sur les applications système et installées. Les API UI Automator vous permettent d'effectuer des opérations telles que l'ouverture du menu "Paramètres" ou du lanceur d'applications sur un appareil de test.
  • Robolectric (Android 4.1, niveau d'API 16 ou supérieur) vous permet de créer des tests locaux qui s'exécutent sur votre poste de travail ou votre environnement d'intégration continue dans une JVM standard, plutôt que sur un émulateur ou un appareil. Elle peut utiliser des API de test Espresso ou Compose pour interagir avec les composants de l'interface utilisateur.

Défauts et synchronisation

La nature asynchrone des applications mobiles et des frameworks rend souvent difficile l'écriture de tests fiables et reproductibles. Lorsqu'un événement utilisateur est injecté, le framework de test doit attendre que l'application ait fini de réagir. Cela peut aller de la modification d'un texte à l'écran à la recréation complète d'une activité. Lorsqu'un test n'a pas de comportement déterministe, il est irrégulier.

Les frameworks modernes tels que Compose ou Espresso sont conçus pour les tests. Il y a donc une certaine garantie que l'UI sera inactive avant la prochaine action ou assertion de test. Il s'agit de la synchronisation.

Tester la synchronisation

Des problèmes peuvent toujours survenir lorsque vous exécutez des opérations asynchrones ou en arrière-plan inconnues du test, telles que le chargement de données à partir d'une base de données ou l'affichage d'animations infinies.

Schéma illustrant une boucle qui vérifie si l'application est inactive avant d'effectuer un test
Figure 1: Tester la synchronisation

Pour améliorer la fiabilité de votre suite de tests, vous pouvez installer un moyen de suivre les opérations en arrière-plan, comme les ressources Espresso Idling. En outre, vous pouvez remplacer les modules pour les versions de test que vous pouvez interroger pour vérifier l'inactivité ou qui améliorent la synchronisation, tels que TestDispatcher pour les coroutines ou RxIdler pour RxJava.

Diagramme illustrant un échec de test lorsque la synchronisation est basée sur l'attente d'une durée déterminée
Figure 2: L'utilisation du sommeil lors des tests entraîne des délais de test lents ou irréguliers.

Architecture et configuration des tests

L'architecture de votre application doit permettre aux tests de remplacer certaines parties de celle-ci pour effectuer des tests doublés. Vous devez utiliser des bibliothèques qui fournissent des utilitaires pour faciliter les tests. Par exemple, vous pouvez remplacer un module de dépôt de données par une version en mémoire qui fournit de fausses données déterministes au test.

Schémas d'architecture pour la production et les tests Le schéma de production montre les sources de données locales et distantes qui fournissent des données au dépôt, lequel les fournit ensuite de manière asynchrone à l'interface utilisateur. Le schéma des tests montre un dépôt fictif qui fournit ses données de manière synchrone à l'interface utilisateur.
Figure 3: Test d'une interface utilisateur en remplaçant ses dépendances par des simulations.

L'approche recommandée pour activer cette fonctionnalité consiste à utiliser l'injection de dépendances. Vous pouvez créer votre propre système manuellement, mais nous vous recommandons d'utiliser un framework d'injection de dépendances comme Hilt pour cela.

Pourquoi effectuer un test automatique ?

Une application Android peut cibler des milliers d'appareils différents avec de nombreux niveaux d'API et facteurs de forme. Le haut niveau de personnalisation que l'OS apporte à l'utilisateur signifie que votre application peut s'afficher de manière incorrecte ou même planter sur certains appareils.

Les tests de l'interface utilisateur vous permettent d'effectuer des tests de compatibilité afin de vérifier le comportement d'une application dans différents contextes. Vous pouvez exécuter les tests de l'interface utilisateur sur des appareils qui varient selon les critères suivants:

  • Niveau d'API: 21, 25 et 30.
  • Paramètres régionaux: anglais, arabe et chinois.
  • Orientation: Portrait, Paysage.

De plus, les applications doivent vérifier le comportement en dehors des téléphones. Vous devez effectuer le test sur des tablettes, des pliables et d'autres appareils.

Ressources supplémentaires

Pour en savoir plus sur la création de tests d'interface utilisateur, consultez les ressources suivantes.

Documentation

Ateliers de programmation