Tester les listes et les adaptateurs

1. Avant de commencer

Dans les précédents ateliers de programmation, vous avez appris à écrire et à exécuter des tests unitaires et des tests d'instrumentation. Cet atelier de programmation présente quelques bonnes pratiques à suivre pour l'écriture des tests et explique comment ajouter des dépendances Gradle spécifiques aux tests. Vous vous entraînerez également à écrire davantage de tests unitaires et tests d'instrumentation.

Conditions préalables

  • Vous avez ouvert un projet existant dans Android Studio.
  • Vous avez écrit des tests unitaires et des tests d'instrumentation dans Android Studio.
  • Vous avez de l'expérience en navigation de projets dans Android Studio.
  • Vous avez déjà utilisé des fichiers build.gradle dans Android Studio.

Points abordés

  • Principes de base liés à l'écriture d'un test
  • Ajouter des dépendances Gradle spécifiques aux tests
  • Tester des listes avec des tests d'instrumentation

Ce dont vous avez besoin

  • Un ordinateur sur lequel est installé Android Studio
  • Code de solution de l'application Affirmations.

Télécharger le code de démarrage pour cet atelier de programmation

Dans cet atelier de programmation, vous ajouterez des tests d'instrumentation au code de solution de l'application Affirmations.

  1. Accédez à la page du dépôt GitHub fournie pour le projet.
  2. Vérifiez que le nom de la branche correspond à celui spécifié dans l'atelier de programmation. Par exemple, dans la capture d'écran suivante, le nom de la branche est main.

1e4c0d2c081a8fd2.png

  1. Sur la page GitHub du projet, cliquez sur le bouton Code pour afficher une fenêtre pop-up.

1debcf330fd04c7b.png

  1. Dans la fenêtre pop-up, cliquez sur le bouton Download ZIP (Télécharger le fichier ZIP) pour enregistrer le projet sur votre ordinateur. Attendez la fin du téléchargement.
  2. Recherchez le fichier sur votre ordinateur (il se trouve probablement dans le dossier Téléchargements).
  3. Double-cliquez sur le fichier ZIP pour le décompresser. Un dossier contenant les fichiers du projet est alors créé.

Ouvrir le projet dans Android Studio

  1. Lancez Android Studio.
  2. Dans la fenêtre Welcome to Android Studio (Bienvenue dans Android Studio), cliquez sur Open (Ouvrir).

d8e9dbdeafe9038a.png

Remarque : Si Android Studio est déjà ouvert, sélectionnez l'option de menu File > Open (Fichier > Ouvrir).

8d1fda7396afe8e5.png

  1. Dans l'explorateur de fichiers, accédez à l'emplacement du dossier du projet décompressé (il se trouve probablement dans le dossier Téléchargements).
  2. Double-cliquez sur le dossier de ce projet.
  3. Attendez qu'Android Studio ouvre le projet.
  4. Cliquez sur le bouton Run (Exécuter) 8de56cba7583251f.png pour créer et exécuter l'application. Assurez-vous qu'elle fonctionne correctement.

2. Présentation de l'application de démarrage

L'application Affirmations se compose d'un seul écran qui présente à l'utilisateur une liste d'images associées à des affirmations.

3. Bonnes pratiques

De par sa conception, le code de test diffère de la logique métier d'une application. En effet, les tests ne sont pas censés contenir de logique. Leur seule fonction est de vérifier l'application. Par conséquent, les tests ne doivent pas avoir d'instructions conditionnelles comme if ou when, ni des instructions de flux de contrôle telles que for ou while. Ils ne doivent pas non plus manipuler les valeurs ni effectuer de calculs réels.

Dans certains cas, il se peut que vos tests nécessitent certains de ces éléments, mais en général, ils sont à éviter. Comme il s'agit du type de logique que nous voulons tester dans notre application, si nous utilisions ce type de code dans un test, il pourrait échouer de la même manière que notre code d'application.

Les tests unitaires doivent seulement appeler l'extrait de code nécessaire au test et vérifier les valeurs ou l'état du code résultant de l'appel de ce code. Les tests de l'interface utilisateur ne doivent porter que sur l'état attendu de l'interface utilisateur. Intégrer ce concept demande parfois du temps, mais c'est normal. Certains sujets qui expliquent davantage ce concept seront abordés dans de futurs ateliers de programmation. En attendant, prêtez attention aux approches que nous adoptons pour écrire les tests afin de bien vous familiariser avec elles.

4. Créer les répertoires de test

Dans un atelier de programmation précédent, vous avez appris à créer un répertoire androidTest pour les tests d'instrumentation. Répétez ce processus pour ce projet pour les répertoires androidTest et test. Le processus est le même pour les deux, à la seule différence que pour le répertoire test, vous devez sélectionner test/java dans le menu déroulant Nouveau répertoire au lieu d'androidTest/java. Créez un package appelé com.example.assertions pour chaque nouveau répertoire.

d762ecd8950e97b2.png

5. Créer une classe de test d'instrumentation

Créez une classe appelée AffirmationsListTests.kt dans androidTest -> com.example.assertions.

Comme avec l'application Dice Roller, Affirmations n'implique qu'une seule activité. Pour tester l'interface utilisateur correspondant à cette activité, nous devons spécifier que nous voulons qu'elle soit initiée. Essayez de vous rappeler comment procéder par vous-même.

  1. Ajoutez un lanceur lde test à la classe que vous venez de créer.
@RunWith(AndroidJUnit4::class)
  1. Créez une règle de scénario d'activité pour l'activité principale.
@get:Rule
val activity = ActivityScenarioRule(MainActivity::class.java)
  1. L'application Affirmations affiche la liste des images et leurs affirmations positives respectives. L'interface utilisateur ne permet aucune interaction avec les éléments (aucun clic ni balayage d'écran). Pour cette application, le test d'instrumentation ne teste donc que des données statiques. Créez une méthode de test appelée scroll_to_item(). N'oubliez pas qu'elle doit être annotée avec la mention @Test.

Ce test doit faire défiler l'écran jusqu'à un élément spécifique de la liste. Nous n'avons pas encore abordé cette approche, car elle implique une méthode à laquelle notre projet ne renvoie pas encore. Avant de poursuivre, nous devons ajouter des dépendances de test.

6. Ajouter des dépendances de test d'instrumentation

Vous devriez déjà savoir comment ajouter des dépendances Gradle à utiliser dans le code de votre application. Gradle permet également d'ajouter des dépendances spécifiques aux tests unitaires et aux tests d'instrumentation. Ouvrez le fichier build.gradle au niveau de l'application. Il se trouve sous app -> build.gradle. Dans la section des dépendances, il existe trois types d'implémentations : implementation, testImplementation et androidTestImplementation.

implementation est destiné aux dépendances qui seront utilisées dans l'application même, testImplementation aux dépendances destinées aux tests unitaires et androidTestImplementation aux dépendances spécifiques aux tests d'instrumentation.

  1. Ajoutez une dépendance pour permettre une interaction avec les éléments RecyclerView dans les tests d'instrumentation. Ajoutez la bibliothèque suivante comme androidTestImplementation :
androidx.test.espresso:espresso-contrib:3.4.0

Les dépendances se présentent comme suit :

dependencies {
    ...
    androidTestImplementation
'androidx.test.espresso:espresso-contrib:3.4.0'
}
  1. Maintenant, synchronisez le projet.

7. Tester l'élément RecyclerView

  1. Une fois le projet synchronisé, revenez au fichier AffirmationsListTests.kt. Fournissez un élément ViewInteraction pour effectuer une action avec onView(). La méthode onView() nécessite la transmission d'un élément ViewMatcher. Utilisez withId() en veillant à transmettre l'ID de l'élément RecyclerView qui a été utilisé pour les affirmations. Appelez maintenant perform() au niveau de l'élément ViewInteraction. C'est là que la dépendance que vous venez d'ajouter entre en jeu. La transmission de RecyclerViewActions.scrollToPosition<RecyclerView.Viewholder>(9) ViewAction peut désormais avoir lieu.
onView(withId(R.id.recycler_view)).perform(
   RecyclerViewActions
       .scrollToPosition<RecyclerView.ViewHolder>(9)
)

Bien qu'il ne soit pas crucial de comprendre la syntaxe de cette ligne, elle mérite d'être examinée. Comme son nom l'indique, RecyclerViewActions est une classe qui permet à vos tests d'effectuer des actions au niveau d'un élément RecyclerView. scrollToPosition() est une méthode statique de la classe RecyclerViewActions, qui fait défiler l'écran jusqu'à une position spécifiée. Cette méthode renvoie un élément générique. Les éléments génériques n'entrent pas dans le cadre de cet atelier de programmation. Dans le cas présent, ils sont assimilables à la méthode scrollToPosition() qui renvoie tous les éléments de RecyclerView, quels qu'ils soient.

Dans notre application, les éléments de RecyclerView sont des objets ViewHolder. Nous plaçons donc une paire de chevrons après l'appel de la méthode et nous spécifions RecyclerView.ViewHolder. Pour finir, nous transmettons la position finale dans la liste (9).

  1. Maintenant que le défilement jusqu'à la position souhaitée de RecyclerView est activé, créez une assertion pour vous assurer que l'interface utilisateur affiche les informations attendues. Assurez-vous qu'une fois que vous avez fait défiler l'écran jusqu'au dernier élément, le texte associé à l'affirmation finale s'affiche. Commencez avec un élément ViewInteraction, mais transmettez cette fois un nouvel objet ViewMatcher (dans ce cas, withText()). Dans cette méthode, transmettez la ressource de chaîne qui contient le texte de la dernière affirmation. La méthode withText() identifie un composant d'interface utilisateur à partir du texte qu'il affiche. Pour ce composant, il vous suffit de vérifier que le texte souhaité est affiché. Pour ce faire, appelez check() au niveau de ViewInteraction. check() nécessite un élément ViewAssertion, pour lequel vous pouvez utiliser la méthode matches(). Pour terminer, confirmez que le composant d'interface utilisateur s'affiche en transmettant la méthode isDisplayed().
onView(withText(R.string.affirmation10))
    .check(matches(isDisplayed()))

Pour en revenir à la remarque sur le codage en dur d'une position vers laquelle vous souhaitez faire défiler l'écran, RecyclerViewActions offre une solution. Si vous n'êtes pas sûr de la longueur de la liste, vous pouvez utiliser l'action scrollTo. La fonction scrollTo nécessite un objet Matcher<View!>! pour rechercher un élément particulier. Plusieurs options sont possible, mais dans le cadre de ce test, utilisez withText. Si vous l'appliquez au test que vous venez d'écrire, le code se présente comme suit :

onView(withId(R.id.recycler_view)).perform(
   RecyclerViewActions
       .scrollTo<RecyclerView.ViewHolder>(
           withText(R.string.affirmation10)
       )
)

onView(withText(R.string.affirmation10))
    .check(matches(isDisplayed())
)

Tout est maintenant prêt pour l'exécution du test. L'appareil ou l'émulateur devrait faire défiler l'écran vers le bas de la liste, puis le test devrait réussir. Pour vous assurer que le résultat du test est exact, remplacez l'ID de chaîne par R.string.affirmation1. Après le défilement, cette ressource de chaîne ne s'affiche pas, et le test devrait échouer.

Plusieurs méthodes sont disponibles dans la classe RecyclerViewActions. Nous vous recommandons de vous pencher sur les méthodes disponibles.

8. Créer une classe de test locale

Créez une classe appelée AffirmationsAdapterTests.kt dans test -> com.example.assertions.

9. Ajouter des dépendances de test locales

  1. Précédemment dans cet atelier de programmation, nous avons abordé trois types différents d'implémentations de dépendances, et vous avez ajouté une dépendance pour les tests d'instrumentation. Vous allez maintenant ajouter une dépendance pour les tests locaux. Accédez à app -> build.gradle, puis ajoutez l'extrait suivant en tant que dépendance du test unitaire :
org.mockito:mockito-core:3.12.4

Les dépendances doivent se présenter comme suit :

dependencies {
    ...
    testImplementation 'org.mockito:mockito-core:3.12.4'
}
  1. Maintenant, synchronisez le projet.

10. Tester l'adaptateur

Cette application spécifique ne se prête pas aux tests unitaires, car il n'y a pas beaucoup de logique à tester. Nous pouvons toutefois nous entraîner un peu en testant différents composants en préparation de tests ultérieurs.

  1. Placez la ligne suivante dans la classe de test unitaire :
private val context = mock(Context::class.java)

La méthode mock() provient de la bibliothèque que nous venons d'implémenter dans notre projet. La simulation fait partie intégrante des tests unitaires, mais elle n'entre pas dans le cadre de cet atelier de programmation. Nous reviendrons plus en détail sur cet aspect dans un atelier distinct. Dans Android, Context est le contexte correspondant à l'état actuel de l'application, mais n'oubliez pas que les tests unitaires s'exécutent sur la machine JVM et non sur un appareil réel. Il n'existe donc pas de Context. La méthode de simulation nous permet de créer une instance "fictive" d'un Context. Elle n'inclut pas de fonctionnalité réelle, mais peut être utilisée pour tester des méthodes qui nécessitent un contexte.

  1. Créez une fonction appelée adapter_size() et annotez-la en tant que test. L'objectif de ce test est de s'assurer que la taille de l'adaptateur correspond à celle de la liste transmise à l'adaptateur. Pour ce faire, créez une instance d'ItemAdapter et transmettez la liste renvoyée par la méthode loadAffirmations() dans la classe Datasource. Vous pouvez également créer une liste et la tester. Pour les tests unitaires, il est recommandé de créer des données spécifiques au test. Nous allons donc créer une liste personnalisée pour ce test.
val data = listOf(
   Affirmation(R.string.affirmation1, R.drawable.image1),
   Affirmation(R.string.affirmation2, R.drawable.image2)
)
  1. Créez maintenant une instance d'ItemAdapter, en transmettant les variables context et data créées aux étapes précédentes.
val adapter = ItemAdapter(context, data)

Les adaptateurs de la vue recycleur ont une méthode appelée getItemCount(), qui renvoie la taille de l'adaptateur. Pour cette application, la méthode se présente comme suit :

/**
* Return the size of your dataset (invoked by the layout manager)
*/
override fun getItemCount() = dataset.size
  1. C'est la méthode qui doit être testée. Assurez-vous que la valeur qu'elle renvoie correspond à la taille de la liste que vous avez créée à l'étape 2. Utilisez la méthode assertEquals(), puis comparez les valeurs de taille de la liste et de l'adaptateur.
assertEquals("ItemAdapter is not the correct size", data.size, adapter.itemCount)

Vous connaissez déjà la méthode assertEquals(), mais il peut être utile d'examiner cette ligne plus en détail. Le premier paramètre est une chaîne qui s'affiche dans le résultat du test en cas d'échec. Le deuxième paramètre correspond à la valeur attendue. Le troisième paramètre correspond à la valeur réelle. La classe de test doit se présenter comme suit :

f81a27f5c1cf055e.png

  1. Maintenant, exécutez le test.

11. Code de solution

12. Félicitations

Cet atelier de programmation vous a permis de vous familiariser avec les points suivants :

  • Vous avez découvert comment ajouter des dépendances spécifiques aux tests.
  • Vous avez appris à interagir avec un élément RecyclerView avec des tests d'instrumentation.
  • Vous avez découvert quelques bonnes pratiques fondamentales pour les tests.