Les défis des tests d'interface utilisateur
Les tests rapides et évolutifs de l'interface utilisateur constituent un défi majeur pour garantir l'absence de bogues dans le développement d'applications mobiles, car les outils d'automatisation des tests ne produisent pas de tests faciles à lire et évolutifs, et les solutions de rechange sont tout aussi fastidieuses. Alors que des outils comme UI Automator ou Espresso utilisant Android Studio ont permis aux ingénieurs de commencer plus facilement à écrire des tests sur Android pour simuler le comportement de l'utilisateur, les tests sont difficiles à comprendre et à gérer à l'échelle. Bien que les tests puissent convenir au début, l'augmentation du nombre de tests rend plus difficile la compréhension du code de test, ce qui pose un problème de maintenance à long terme. Les outils d'automatisation des tests peuvent produire un code de test dans lequel chaque action est décrite en trois ou quatre lignes d'instructions, au lieu de lignes concises avec des descripteurs clairs, en utilisant le langage des affaires, comme le montre la figure 1 ci-dessous : L'alternative à l'utilisation de plates-formes de test automatisées consiste à faire appel à des testeurs manuels. Malheureusement, cette solution ne permet pas vraiment de gagner du temps, car les testeurs manuels ont besoin de transferts de connaissances et la gestion de la délégation prend presque autant de temps et d'efforts que si les développeurs effectuaient eux-mêmes les tests. En outre, les tests manuels ne sont pas aussi précis que les tests automatisés, car ils laissent place à davantage d'erreurs humaines, et ils ne sont pas rentables pour les régressions à haut volume.Comment un modèle de conception peut aider à l'automatisation de l'interface utilisateur
Les problèmes liés aux tests manuels de l'interface utilisateur peuvent être résolus en choisissant un bon modèle de conception et le bon cadre pour les tests de l'interface utilisateur. Un bon cadre de test d'automatisation doit permettre aux ingénieurs d'écrire des tests qui sont.. :- Facile à comprendre et à lire
- Rapidement rédigé
- Maintenable
- Évolutif
- Chaque action peut être effectuée en une seule ligne
- Les détails sont extraits à l'intérieur d'une fonction
- Cette fonction peut être réutilisée chaque fois que cette action est à nouveau requise
- The test is still not clearly showing it’s intent; instead it looks more like coded instructions
- Il y aura encore beaucoup de duplication de code, ce qui n'est pas idéal
Utilisation d'une interface fluide pour mettre en évidence la logique commerciale
Un modèle de conception Fluent nous offre le meilleur des deux mondes, car il démontre une intention claire en utilisant un langage spécifique au domaine. Une interface fluide est une API orientée objet dont la conception repose largement sur l'enchaînement de méthodes. L'objectif est d'améliorer la lisibilité du code en utilisant un langage spécifique au domaine, permettant de relayer le contexte d'instruction d'un appel ultérieur.Comment un modèle de conception Fluent démontre une intention claire
Les modèles de conception doivent avoir une intention claire et utiliser un langage spécifique au domaine qui peut presque être lu comme un langage conversationnel. Une interface fluide répond à ces critères car elle nous permet d'utiliser des noms d'API fluides et un langage spécifique au domaine. Les avantages de l'utilisation d'un modèle de conception Fluent sont les suivants :- Lorsque le code de test est facile à comprendre, il est facile à étendre et à réutiliser.
- La facilité d'utilisation aidera les développeurs à travailler plus rapidement et à être plus confiants lors de la rédaction des tests.
- Le modèle de conception est indépendant des outils sous-jacents tels que UI Automator ou Espresso.
Utilisation d'un modèle de conception Fluent pour construire l'automatisation des tests de l'interface utilisateur de l'application Dasher
Ici, à Doordash, nous avons utilisé TestRail pour tester manuellement l'application avant chaque mise en production. Il fallait auparavant trois ingénieurs logiciels et un ingénieur chargé de l'assurance qualité pour passer en revue les tests TestRail et une demi-journée chacun pour les exécuter, ce qui prenait deux journées de travail complètes. Ce processus limitait le nombre de versions de l'application à toutes les deux semaines. La mise en place d'un nouveau cadre d'automatisation de l'interface utilisateur pour Android a permis d'éliminer ces points douloureux pour les cycles de publication. Nous allons maintenant entrer un peu plus dans les détails pour expliquer l'approche et les outils que nous avons utilisés, l'architecture globale de haut niveau de la solution, et partager quelques bonnes pratiques.Notre approche de l'utilisation des modèles de conception Fluent
En général, chaque scénario de test de l'interface utilisateur implique des interactions avec des activités et des écrans ; sur chaque écran, l'utilisateur effectuera une action et attendra un comportement en conséquence. Nous utilisons alors assertions pour vérifier les résultats. Pour réaliser ces tests, nous structurons le code de test de manière à ce que chaque écran encapsule les actions qui sont effectuées sur cet écran et puisse vérifier le comportement après avoir effectué ces actions. Toutes les interactions sont nommées dans un langage spécifique au domaine en utilisant l'interface du modèle de conception Fluent, comme le montre la figure 3 ci-dessous :Suite de tests
Le choix des outils joue un rôle important dans l'amélioration de la productivité des développeurs. Nous estimons que nos outils sont faciles à utiliser et qu'ils bénéficient d'une bonne assistance en ligne. Outils de test de l'interface utilisateur : Avant d'élaborer nos procédures de test de l'interface utilisateur, nous avons examiné différents outils permettant de rédiger des tests de l'interface utilisateur, tels que Appium, a third-party tool. However, we found that Android‚Äôs native tools were easier to use and had better support. There are two UI testing tools supported by Google, which can be run separately or together, since they run under the same instrumentation test runner:- UI Automator - UI Automator est un cadre de test d'interface utilisateur adapté aux tests fonctionnels d'interface utilisateur inter-applications à travers le système et les applications installées.
- Espresso - L'un des principaux avantages de l'utilisation d'Espresso est qu'il permet de synchroniser automatiquement les actions de test avec l'interface utilisateur de l'application testée. Espresso détecte lorsque le thread principal est inactif, ce qui lui permet d'exécuter les commandes de test au moment opportun, améliorant ainsi la fiabilité des tests.
- Studio Android: Pour les développeurs qui pensent que les tests d'interface utilisateur font partie intégrante du développement d'une application, Android Studio leur facilitera la vie. Il permet d'exécuter les tests unitaires, les tests de l'interface utilisateur Android et l'application elle-même dans le même environnement de développement. Il permet également de structurer les paquets de telle sorte que le code de l'application et les tests correspondants (tests unitaires et tests de l'interface utilisateur) peuvent résider dans le même référentiel, ce qui facilite la maintenance des versions du code de l'application et des tests correspondants.
- Les tests d'interface utilisateur sont généralement exécutés sur un appareil réel ou sur un émulateur afin d'imiter le scénario de test. Pour la plupart de nos tests, nous utilisons des émulateurs pour les configurations et tailles d'appareils les plus courantes.
- Bitrise est l'un des outils CI/CD les plus populaires sur le cloud qui permet une mise à l'échelle et une facilité d'utilisation pour la mise en place d'environnements de test. En particulier pour les tests d'interface utilisateur, il permet l'intégration à la fois d'un parc d'appareils et d'appareils virtuels et est devenu un outil facile pour mettre en place un environnement de construction et de test pour les développeurs.
Processus de test
Nous écrivons des scénarios de test, illustrés dans la figure 3 ci-dessus, dans un langage spécifique au domaine, en suivant une approche axée sur le comportement. Ces tests utilisent l'API de configuration des tests pour créer l'environnement d'un test particulier et utilisent des objets d'écran qui interagissent avec les écrans et vérifient les actions. L'interaction et la vérification des écrans sont finalement réalisées par un outil d'automatisation des tests, tel que UI Automator, Espresso ou tout autre outil similaire. To understand this process, let‚Äôs look at an example for the login flow of an app using a Fluent design pattern and our testing architecture described above. Test de connexion : Le test de la figure 4 est écrit en utilisant le modèle de conception Fluent, et la classe de base qui permet ce modèle s'appelle Screen.kt. Le code de Screen.kt est présenté dans la figure 5 ci-dessous : All the screen classes extend this class and follow the pattern of returning itself for each interaction/verification function, thereby passing the context along. The inline generic method ‚Äú<reified T: Screen> on()‚Äù is used to switch context from one screen to another. An example of the ‚ÄúScreen‚Äù implementation is shown in Figure 6, below: L'implémentation ci-dessus utilise l'outil sous-jacent, UI Automator, pour interagir avec l'écran. Bien que cet exemple utilise UI Automator, il peut être remplacé par Espresso ou tout autre outil similaire sans affecter la logique d'entreprise ou les attentes en matière de test.Révision de la structure des dossiers
Pour les codeurs propres et les concepteurs de logiciels, la principale propriété d'un paquet est la possibilité d'avoir un nom significatif qui décrit son objectif et sa raison d'être. C'est pourquoi nous avons organisé nos paquets comme suit :- test : Contient tous les tests pour différents scénarios
- écran : Contient tous les écrans de l'application et les interactions correspondantes.
- UI Automator/Espresso : contient des classes d'outils permettant d'effectuer des interactions à l'écran et de vérifier les comportements.
- utils : API commune permettant de configurer l'environnement pour l'exécution d'un test, par exemple la création et l'attribution d'ordres avant le démarrage de Dashing. Elle contient également d'autres fonctions utilitaires communes.
Bonnes pratiques pour l'utilisation de cette approche
En écrivant ces tests, nous avons développé quelques bonnes pratiques qui nous ont aidés à garder notre code propre, lisible et extensible. En voici quelques-unes :- Convention de nommage : Même pour la classe UIAutomator, nous continuons à utiliser le modèle de conception Fluent, qui se lit comme un langage spécifique au domaine.
- UiAutomator.kt : cette classe comportera essentiellement deux types de fonctions : toute action de l'utilisateur sur l'écran et la vérification du comportement.
- Verification function name uses this pattern: hasViewBy<Class,Text,ContenDesc,ResourceId etc that identifies the view>
- Action function name has this pattern: <click,swipe,scroll,set><Button/View>By<Button/View identifies>
- L'écran : Il est très important d'utiliser ici le modèle de conception Fluent et de trouver un nom correct pour les fonctions afin qu'elles soient plus fluides lors de la lecture du test.
- Le nom de la classe d'écran correspond à la fonction de l'écran, par exemple PickUpItemScreen().
- Le nom de la fonction de vérification est rédigé dans un langage spécifique au domaine, par exemple verifyAmount(), verifySignatureStepComplete(), verifyCompleteStepsGetCxSignature(), etc.
- Le nom de la fonction d'action est également dans un langage spécifique au domaine, par exemple clickStartCateringSetup(), slideBeforeCateringSetupComplete() etc.
- UiAutomator.kt : cette classe comportera essentiellement deux types de fonctions : toute action de l'utilisateur sur l'écran et la vérification du comportement.
- Nous devrions toujours ajouter un journal dans chaque fonction de la classe d'écran, ce qui permet de dépanner plus rapidement les journaux CI/CD.
- Toute boîte de dialogue/feuille de bas de page pertinente pour un écran est définie comme une classe imbriquée de l'écran parent.
- Toutes les vérifications auraient dû faire l'objet d'une assertion accompagnée d'un message de journal indiquant clairement la raison de l'échec de l'assertion, comme le montre la figure 8 ci-dessous :
Les modèles de conception fluides augmentent la vitesse des développeurs
Une fois le cadre initial mis en place, nous avons terminé 70 % de nos tests de régression en deux mois. Voici quelques-uns des résultats :- La couverture de notre code est passée de 0 % à ~40 %.
- Nos tests manuels ont été quatre fois plus rapides, passant de 16 à 4 heures.
- Les cycles de publication sont passés d'une version toutes les deux semaines à des versions hebdomadaires, et nous pouvons effectuer des tests de régression à tout moment.
- L'équipe est plus productive car il suffit d'écrire les tests pour les nouvelles fonctionnalités ou de mettre à jour les fonctionnalités existantes, ce qui est beaucoup plus rapide à développer.