Les tests d'interface utilisateur vérifient la correction de l'affichage et de l'interaction des éléments de l'interface utilisateur d'une application mobile — boutons, champs de texte, listes et composants de navigation. Contrairement aux tests unitaires qui vérifient la logique métier, les tests UI simulent des actions utilisateur : touches, glissements, saisie de texte et vérifient la réponse de l'interface. Selon une étude de Android Developers, 2024, les tests UI couvrent 70 % des scénarios utilisateur critiques et permettent de détecter des défauts de mise en page inaccessibles aux vérifications logiques.
Points clés
Les tests UI sont un type de vérification automatisée où le code de test interagit avec l'interface graphique de l'application de la même manière qu'un utilisateur réel. Le test trouve un élément à l'écran — un bouton, un champ de texte, une liste — effectue une action dessus et vérifie la réponse attendue de l'interface. Par exemple, après avoir saisi un mot de passe incorrect, un test UI vérifie qu'un message d'erreur avec le texte correct apparaît à l'écran.
La principale différence entre les tests UI et les autres types d'automatisation est qu'ils fonctionnent via la couche d'accessibilité du système d'exploitation, et non via les API internes de l'application. Cela signifie que les tests UI voient l'interface exactement comme un utilisateur et un lecteur d'écran. Grâce à cela, les tests UI vérifient non seulement la fonctionnalité mais aussi l'accessibilité des éléments — la conformité aux exigences WCAG.
Selon l'enquête JetBrains Developer Ecosystem 2023, 58 % des équipes mobiles utilisent des tests UI dans leur pipeline CI/CD. La couverture moyenne des tests UI dans les projets commerciaux est de 30 à 40 % des écrans de l'application. Les projets avec des tests UI reçoivent 25 % d'avis négatifs en moins dans les magasins d'applications liés aux crashes d'interface.
La principale différence entre les tests UI et les tests unitaires est le niveau d'abstraction. Les tests unitaires travaillent avec des classes et des fonctions individuelles, isolées du framework Android ou iOS. Ils s'exécutent sur la JVM (pour Android) sans lancer d'émulateur et prennent des millisecondes. Les tests UI s'exécutent sur un appareil réel ou un émulateur, interagissent avec les services système et prennent des secondes ou des minutes par scénario.
Le public cible des tests diffère également. Les tests UI vérifient des scénarios utilisateur complets — inscription, passage de commande, recherche. Les tests unitaires couvrent la logique métier : calculs, validation, transformation de données. Un test UI ne vérifie pas la correction du calcul de taxe — il vérifie que le montant total s'affiche à l'écran. Le calcul lui-même est vérifié par un test unitaire.
Selon le Google Testing Blog (2020), la proportion idéale de tests dans un projet suit la règle de la pyramide de tests : 70 % de tests unitaires, 20 % de tests d'intégration et 10 % de tests UI. La violation de cette proportion en faveur des tests UI entraîne une augmentation du temps d'exécution et une fragilité de la suite de tests, car les tests UI sont sensibles aux changements de mise en page des écrans.
Pour Android, le framework dominant est Espresso — une bibliothèque de Google intégrée à AndroidX Test. Espresso se synchronise automatiquement avec le thread UI, attendant la fin des animations et des tâches en arrière-plan avant d'exécuter la vérification suivante. Pour Jetpack Compose, l'extension Compose UI Test est utilisée, fonctionnant via des nœuds sémantiques au lieu des identifiants de vue traditionnels.
Pour iOS, l'outil principal est XCUITest, qui fait partie d'Xcode. Les tests sont écrits en Swift et utilisent des identifiants d'accessibilité pour trouver les éléments. XCUITest prend en charge l'enregistrement de tests via la fonction d'enregistrement et l'intégration avec les systèmes CI via xcodebuild. Pour les projets multiplateformes, Appium est utilisé, basé sur le protocole WebDriver et permettant d'exécuter les mêmes tests sur Android et iOS avec des modifications minimales du code.
Espresso fonctionne avec le système de vues traditionnel via onView et les identifiants de ressources. Compose UI Test utilise une couche sémantique, rendant les tests moins dépendants de la hiérarchie des vues. Par exemple, la recherche d'un bouton dans Espresso : onView(withId(R.id.submit)), dans Compose : onNodeWithTag(“submit”). Les tests Compose gèrent automatiquement la recomposition et ne nécessitent pas d'attentes explicites d'état inactif.
XCUITest utilise XCUIApplication comme point d'entrée. Chaque élément d'interface est trouvé via des propriétés d'accessibilité : accessibilityIdentifier pour l'accès programmatique et accessibilityLabel pour VoiceOver. Le framework prend en charge l'enregistrement de tests via la fonction d'enregistrement d'Xcode — le développeur effectue des actions sur le simulateur et Xcode génère le code de test. Les tests prêts sont exécutés via xcodebuild test.
Appium est basé sur le protocole WebDriver et prend en charge tout langage : Java, Python, JavaScript. Les stratégies de recherche d'éléments incluent id, xpath, class name et accessibility id. Appium nécessite l'installation d'un serveur et la configuration de Desired Capabilities — platformName, deviceName, appPackage. Une alternative est Maestro, qui utilise des scénarios YAML et ne nécessite pas de compilation de code de test.
Examinons des tests UI pour le même scénario — connexion à l'application — sur trois frameworks différents : Espresso pour Android, XCUITest pour iOS et Appium pour l'approche multiplateforme. Scénario : saisir l'identifiant et le mot de passe, appuyer sur le bouton de connexion, vérifier l'affichage du message de bienvenue.
Un test Espresso utilise onView pour trouver un élément par son identifiant et perform pour exécuter une action. La méthode check avec le matcher isDisplayed confirme que l'élément est visible à l'écran.
@RunWith(AndroidJUnit4::class)
class LoginUiTest {
@Rule
@JvmField
val composeTestRule = createComposeRule()
@Test
fun login_withValidCredentials_showsWelcome() {
composeTestRule
.onNodeWithTag("emailField")
.performTextInput("user@example.com")
composeTestRule
.onNodeWithTag("passwordField")
.performTextInput("secret123")
composeTestRule
.onNodeWithTag("loginButton")
.performClick()
composeTestRule
.onNodeWithText("Bienvenue, Utilisateur !")
.assertIsDisplayed()
}
}
XCUITest utilise XCUIApplication pour accéder aux éléments d'interface via des identifiants d'accessibilité. Les méthodes tap() et exists fournissent l'interaction et la vérification.
class LoginUITests: XCTestCase {
let app = XCUIApplication()
override func setUp() {
continueAfterFailure = false
app.launch()
}
func testLogin_withValidCredentials_showsWelcome() {
app.textFields["emailField"].tap()
app.textFields["emailField"].typeText("user@example.com")
app.secureTextFields["passwordField"].tap()
app.secureTextFields["passwordField"].typeText("secret123")
app.buttons["loginButton"].tap()
XCTAssertTrue(app.staticTexts["Welcome, User!"].exists)
}
}
Le premier principe — utilisez des identifiants d'accessibilité au lieu d'étiquettes textuelles pour trouver les éléments. Le texte d'un bouton peut changer lors de la localisation, tandis que l'identifiant reste stable. Sous Android, il s'agit de la propriété contentDescription ; sous iOS — accessibilityIdentifier. Cette approche rend les tests indépendants de la langue de l'interface et réduit les coûts de maintenance lors des changements de copywriting.
Évitez sleep() et les délais fixes — utilisez les mécanismes d'attente intégrés du framework. Espresso attend automatiquement la fin des animations et des tâches en arrière-plan. XCUITest fournit XCTAssertTrue avec un timeout. Les pauses explicites rendent les tests plus lents et plus instables, en particulier sur les appareils lents dans un environnement CI.
Regroupez les tests par criticité : les tests smoke (3 à 5 scénarios clés) sont exécutés à chaque commit, la suite complète de tests UI est exécutée avant la publication. Selon le Google Testing Blog (2022), les tests UI qui prennent plus de 30 minutes dans le CI réduisent la fréquence d'exécution de 40 %, diminuant leur efficacité en tant qu'outil de détection précoce des régressions.
Les tests UI ont plusieurs limitations. Sensibilité aux changements de mise en page : modifier un identifiant, une hiérarchie ou un type d'élément casse le test même si la fonctionnalité reste inchangée. La solution consiste à utiliser le modèle Page Object, qui centralise les sélecteurs d'éléments dans des classes séparées. Lorsque la mise en page change, un seul fichier Page Object est corrigé, et non des dizaines de tests.
Temps d'exécution : l'exécution sur un appareil réel ou un émulateur prend 10 à 50 fois plus de temps qu'un test unitaire. La solution consiste à exécuter les tests UI en parallèle sur plusieurs appareils via Firebase Test Lab ou AWS Device Farm. L'instabilité (flakiness) est un problème courant des exécutions CI, causé par les animations, les délais réseau ou l'état de l'émulateur. Pour lutter contre l'instabilité, des tentatives automatiques de tests échoués et une analyse de stabilité de chaque scénario de test sont utilisées.
Foire aux questions
Pour un écran moyen, 3 à 5 tests UI suffisent : happy path, validation des erreurs, état vide, changement d'orientation et vérification d'accessibilité. Les écrans complexes avec de multiples états — formulaires de commande, paramètres — peuvent nécessiter 10 à 15 tests pour une couverture complète des scénarios clés.
Oui, Appium et Maestro permettent d'exécuter les mêmes scénarios sur les deux plateformes. Cependant, les frameworks natifs — Espresso et XCUITest — offrent une meilleure stabilité, vitesse et accès aux fonctionnalités spécifiques à la plateforme qui ne sont pas disponibles via les proxys WebDriver.
Pour Compose, la bibliothèque Compose UI Test avec des correspondants sémantiques est utilisée : onNodeWithText, onNodeWithTag, onNodeWithContentDescription. La couche sémantique de Compose abstrait la hiérarchie des vues, rendant les tests moins fragiles par rapport à Espresso traditionnel pour le système de vues.
L'exécution de base des tests UI se fait sur des émulateurs dans le CI — c'est rapide et peu coûteux. La vérification finale avant la publication doit être effectuée sur des appareils physiques via Firebase Test Lab pour prendre en compte les caractéristiques du matériel réel : différentes résolutions, versions d'OS et performances.
Utilisez l'exécution parallèle sur plusieurs appareils, désactivez les animations sur l'émulateur via les options développeur, construisez une architecture de tests modulaire et exécutez la suite smoke à chaque commit, l'exécution de régression complète étant planifiée ou effectuée avant la publication.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi