Tests fonctionnels sur les applications Android : Introduction à Espresso

Je vous propose ici une introduction à l’API Espresso. Elle permet de réaliser des tests fonctionnels sur les applications Android de manière rapide et facile.

Espresso, c’est quoi ?

L’API Espresso (appartenant à Android Testing Support Library) permet de mettre en place des tests fonctionnels (Tests de Non Régression) en simulant les interactions possibles des utilisateurs avec l’application. Elle est légère et facile à prendre en main.

Lors de la création des tests, le développeur va pouvoir réaliser un certain nombre d’actions comme la récupération d’un composant, faire défiler une liste, cliquer sur un composant ou vérifier le texte affiché.

Les tests Espresso sur Android doivent être exécutés sur un terminal physique en USB ou via un émulateur car ils nécessitent une instance du système Android. Il est également possible d’utiliser Firebase Test Lab qui permet d’exécuter les tests sur une grande variété de périphériques physiques et virtuels simultanément.

Les pré-requis

Afin de pouvoir utiliser Espresso ainsi que ses outils, il vous faudra Android Studio 2.2 ou supérieur.

De plus, vous devrez avoir un terminal ou émulateur ayant une version Android 4.4 Kit Kat (API Level 19) ou supérieur.

Configuration

Pour importer Espresso, vous aurez besoin de la configuration suivante.

app/build.gradle

android {
	  ...
	  defaultConfig {
		  ...
		  testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
	  }

	 lintOptions {
		abortOnError false
	 }

	  packagingOptions {
		  exclude 'LICENSE.txt'
	  }
}
    dependencies {
  	     ...
	 androidTestCompile 'com.android.support.test:runner:0.5'
	 androidTestCompile 'com.android.support.test:rules:0.5'
	 androidTestCompile 'com.android.support:support-annotations:25.3.1'
	 androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
	 androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
}

Les composants d’Espresso

Les tests Espresso peuvent être mis en place sur des applications à partir d’Android 2.2 Froyo, ils sont principalement construits à partir des composants suivants :

  • Espresso :  C’est le point d’entrée pour les interactions avec vos différentes vues. Elle propose les méthodes onView() et onData() qui vous permettront de récupérer vos composants UI et vos données dans les AdapterView.
  • ViewMatchers : C’est une collection d’objets Matcher<? super View>. Elle permet de récupérer une vue à l’aide d’une ou plusieurs méthodes que l’on peut passer dans onView().
  • ViewActions : C’est une collection d’objets ViewAction pouvant être passés dans la méthode ViewInteraction.perform() afin de pouvoir effectuer des actions sur une vue (Exemple : le clic, le scroll …).
  • ViewAssertions : C’est une collection d’objets ViewAssertion pouvant être passés dans la méthode ViewInteraction.check() afin de pouvoir comparer 2 objets. C’est ici que le développeur va comparer des états, des chaînes de caractères … Avant et/ou après des actions qu’il aura pu effectuer.

Exemple

onView(withId(R.id.myview)) // withId(R.id.my_view) est un ViewMatcher
     	.perform(click())  // click() est un ViewAction
     	.check(matches(isDisplayed())); // matches(isDisplayed()) est un ViewAssertion

Les ViewAssertions personnalisées

En plus des ViewAssertions proposées par Espresso, il est également possible d’en créer. Prenons comme exemple la couleur d’un texte.

onView(withId(R.id.myedittext)).check(matches(withTextColor(Color.BLACK)));

  public static Matcher<View> withTextColor(final int color) {
	 Checks.checkNotNull(color);
	 return new BoundedMatcher<View, TextView>(TextView.class) {
		 @Override
		 public void describeTo(Description description) {
			 description.appendText("with text color: ");
		 }
	   
		 @Override
		 protected boolean matchesSafely(TextView warning) {
			 return color == warning.getCurrentTextColor();
		 }
	 };
}

Les Extras data

Vous souhaitez tester une activité en particulier mais elle nécessite des Extras data (informations que l’on souhaite faire passer d’une Activity à l’autre en utilisant l’Intent). N’ayez pas peur, on va les créer.

Exemple

@Rule
  public ActivityTestRule<LoginActivity_> rule = new ActivityTestRule<>(LoginActivity_.class, true, false);

  @Before
  public void initIntent() {
	 // Complete intent paramaters
	 Intent intent = new Intent();
	 intent.putExtra("myExtra", “Hello World”);
	 rule.launchActivity(intent);
  }	

Vérifier le passage à une nouvelle Activity

Il est déconseillé de réaliser les tests sur 2 Activity différentes dans une même classe, néanmoins il est possible de vérifier si, lors d’un clic sur un bouton, on est bien passé à l’Activity suivante.

Cette vérification peut être effectuée de la façon suivante :

  • Activity 1 : cliquez sur un élément.
  • Activity 2 : vérifiez que l’un des éléments de cette nouvelle vue existe bien.
  • Activity 2 : revenez sur la première Activity avec pressBack().
  • Activity 1 : vérifiez que l’un des éléments de l’Activity 1 existe bien.

Exemple

ViewInteraction profileId = onView(withId(R.id.profil_id));
  profileId.check(matches(withText("ID client : 123456")));

  ViewInteraction modifyPwd = onView(withId(R.id.modify_password));
  modifyPwd.check(matches(isClickable()));

  modifyPwd.perform(click());

  // New Activity : ChangePasswordActivity
  ViewInteraction textConfirmPwd = onView(withId(R.id.forget_pwd_confirm));
  textConfirmPwd.check(matches(withText(R.string.confirm_pwd)));

  pressBack();

  // Go back to ProfilActivity
  profileId.check(matches(withText("ID client : 123456")));

Plus besoin d’écrire

Maintenant que vous avez les bases pour créer vos tests vous-même, je vais vous parler de Espresso Test Recorder (à partir d’Android Studio 2.2 et Android 4.4 Kit Kat). C’est un outil qui vous permet de générer des tests sans avoir à les écrire (il est caché dans Run > Record Espresso Test).

Tout comme l’exécution des tests, il est nécessaire d’avoir un terminal physique en USB ou un émulateur.

Avant de commencer l’enregistrement, l’application va être buildée sur le terminal sélectionné. Puis la fenêtre “Record your test” apparaît, c’est à ce moment que vous allez pouvoir commencer à interagir avec l’application et voir apparaître vos actions dans la fenêtre prévue à cet effet.

Pendant l’enregistrement, il est possible de sélectionner un composant et de lui ajouter l’une des 3 types d’assertion :

  • text is : Vérifier le contenu du composant
  • exists : Vérifier que le composant existe dans la vue sélectionnée
  • does not exist : Vérifier que le composant n’existe pas dans la vue sélectionnée

Tout ce qui était listé dans la fenêtre “Record your test” sera généré automatiquement dans un fichier identique à ceux que vous créez lorsque que vous réalisez vos tests manuellement. Il ne restera plus qu’à l’exécuter (attention, la transition entre les Activity n’est pas prise en compte dans les tests).

Une vidéo de démonstration est disponible ici.

Exemple de script généré par le recorder

@LargeTest
  @RunWith(AndroidJUnit4.class)
  public class RecordEspressoTest {

	 @Rule
	 public ActivityTestRule<LoginActivity_> mActivityTestRule = new ActivityTestRule<>(LoginActivity_.class);

	 @Test
	 public void recordEspressoTest() {
		 // Added a sleep statement to match the app's execution delay.
		 // The recommended way to handle such scenarios is to use Espresso idling resources:
		 // https://google.github.io/android-testing-support-library/docs/espresso/idling-resource/index.html

		 ViewInteraction editText = onView(
				 allOf(withId(R.id.idField),
						 withParent(withId(R.id.layout_input))));
		 editText.perform(scrollTo(), replaceText("myLogin"), closeSoftKeyboard());

		 ViewInteraction editText2 = onView(
				 allOf(withId(R.id.passField),
						 withParent(withId(R.id.layout_input))));
		 editText2.perform(scrollTo(), replaceText("myPassword"), closeSoftKeyboard());

		 ViewInteraction switch = onView(
				 allOf(withId(R.id.switch_connected), withText("Se souvenir de mon identifiant"),
						 withParent(withId(R.id.layout_input))));
		 switch.perform(scrollTo(), click());

		 ViewInteraction button = onView(
				 allOf(withId(R.id.buttonConnexion), withText("Se connecter"),
						 withParent(withId(R.id.layout_buttons))));
		 button.perform(scrollTo(), click());
	 }
  }

Conclusion

Il n’y a plus besoin d’avoir de connaissances poussées pour intégrer des tests Espresso. Il suffit d’un peu de documentation, d’étudier le fichier généré grâce à Espresso Test Recorder et vous allez pouvoir vous perfectionner au fur et à mesure du temps.

Que ce soit pour les débutants ou confirmés, le gain de temps n’est pas négligeable notamment grâce à Espresso Test Recorder. Il va permettre de créer une structure de tests qui ne demandera que des modifications principalement pour les assertions qui n’ont pas pu être réalisées avec l’outil.

Le document ci-dessous référence les principales méthodes utilisées. Pour la liste complète, il faut vous rendre sur les documentations respectives (ViewMatchers, ViewActions,  ViewAssertions …) disponibles ici.

Le format pdf est disponible ici