La bibliothèque de test des actions dans les applications (AATL) permet aux développeurs de tester le traitement des actions dans les applications par programmation, en automatisant les tests normalement effectués à l'aide de requêtes vocales réelles ou via l'outil de test des actions dans les applications.
Cette bibliothèque permet de s'assurer que la configuration shortcut.xml
est correcte et que l'appel d'intent Android décrit aboutit. La bibliothèque de test des actions dans les applications fournit un mécanisme permettant de tester la capacité de votre application à traiter certains intents et paramètres de l'Assistant Google, en les convertissant en lien profond ou en intent Android, pouvant faire l'objet d'une assertion ou être utilisé pour instancier une activité Android.
Les tests sont effectués sous la forme de tests unitaires Robolectric ou de tests d'instrumentation dans l'environnement Android. Cela permet aux développeurs de tester de manière exhaustive leur application en émulant son comportement réel. Pour tester le traitement des intents intégrés, des intents personnalisés ou des liens profonds, vous pouvez utiliser n'importe quel framework de test d'instrumentation (UI Automator, Espresso, JUnit4, Appium, Detox, Calabash).
Si l'application est multilingue, les développeurs peuvent vérifier son fonctionnement dans différents paramètres régionaux.
Fonctionnement
Pour intégrer la bibliothèque de test des actions dans les applications, les développeurs doivent créer ou mettre à jour des tests Robolectric ou instrumentés existants au niveau du module app
de l'application.
Le code de test contient les éléments suivants :
- Initialisation de l'instance de bibliothèque, dans la méthode de configuration courante ou dans des scénarios de test individuels.
- Chaque test appelle la méthode
fulfill
de l'instance de la bibliothèque pour générer le résultat de la création de l'intent. - Le développeur effectue ensuite une assertion du lien profond ou déclenche le traitement de l'application, puis exécute une validation personnalisée sur l'état de l'application.
Configuration requise
Pour utiliser la bibliothèque de test, vous devez configurer l'application avant de lui ajouter les tests.
Configuration
Pour utiliser la bibliothèque de test des actions dans les applications, assurez-vous que votre application est configurée comme suit :
- Installez le plug-in Android Gradle (AGP).
- Ajoutez un fichier
shortcuts.xml
dans le dossierres/xml
du moduleapp
. - Assurez-vous qu'
AndroidManifest.xml
inclut<meta-data android:name="android.app.shortcuts" android:resource=”@xml/shortcuts” />
sous :- la balise
<application>
- la balise
<activity>
du lanceur
- la balise
- Placez l'élément
<capability>
dans l'élément<shortcuts>
deshortcuts.xml
.
Ajouter des dépendances de la bibliothèque de test des actions dans les applications
Ajoutez le dépôt Google à la liste des dépôts de projets dans
settings.gradle
:allprojects { repositories { … google() } }
Dans le fichier
build.gradle
du module d'application, ajoutez les dépendances AATL :androidTestImplementation 'com.google.assistant.appactions:testing:1.0.0'
Veillez à utiliser le numéro de version de la bibliothèque que vous avez téléchargée.
Créer des tests d'intégration
Créez des tests sous
app/src/androidTest
. Pour les tests Robolectric, créez-les sousapp/src/test
:Kotlin
import android.content.Context import android.content.Intent import android.widget.TextView import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ActivityScenario import com.google.assistant.appactions.testing.aatl.AppActionsTestManager import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentIntentResult import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentResult import com.google.assistant.appactions.testing.aatl.fulfillment.FulfillmentType import com.google.common.collect.ImmutableMap import org.junit.Assert.assertEquals import org.junit.Before import org.junit.runner.RunWith import org.junit.Test import org.robolectric.RobolectricTestRunner … @Test fun IntentTestExample() { val intentParams = mapOf("feature" to "settings") val intentName = "actions.intent.OPEN_APP_FEATURE" val result = aatl.fulfill(intentName, intentParams) assertEquals(FulfillmentType.INTENT, result.getFulfillmentType()) val intentResult = result as AppActionsFulfillmentIntentResult val intent = intentResult.intent // Developer can choose to assert different relevant properties of the returned intent, such as the action, activity, package, scheme and so on assertEquals("youtube", intent.scheme) assertEquals("settings", intent.getStringExtra("featureParam")) assertEquals("actions.intent.OPEN_APP_FEATURE", intent.action) assertEquals("com.google.android.youtube/.MainActivity", intent.component.flattenToShortString()) assertEquals("com.google.myapp", intent.package) // Developers can choose to use returned Android Intent to launch and assess the activity. Below are examples for how it will look like for Robolectric and Espresso tests. // Please note that the below part is just a possible example of how Android tests are validating Activity functionality correctness for given Android Intent. // Robolectric example: val activity = Robolectric.buildActivity(MainActivity::class.java, intentResult.intent).create().resume().get() val title: TextView = activity.findViewById(R.id.startActivityTitle) assertEquals(title?.text?.toString(), "Launching…") }
Java
import android.content.Context; import android.content.Intent; import android.widget.TextView; import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ActivityScenario; import com.google.assistant.appactions.testing.aatl.AppActionsTestManager; import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentIntentResult; import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentResult; import com.google.assistant.appactions.testing.aatl.fulfillment.FulfillmentType; import com.google.common.collect.ImmutableMap; import org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.runner.RunWith; import org.junit.Test; import org.robolectric.RobolectricTestRunner; ... @Test public void IntentTestExample() throws Exception { Map<String, String> intentParams = ImmutableMap.of("feature", "settings"); String intentName = "actions.intent.OPEN_APP_FEATURE"; AppActionsFulfillmentResult result = aatl.fulfill(intentName, intentParams); assertEquals(FulfillmentType.INTENT, result.getFulfillmentType()); AppActionsFulfillmentIntentResult intentResult = (AppActionsFulfillmentIntentResult) result; Intent intent = intentResult.getIntent(); // Developer can choose to assert different relevant properties of the returned intent, such as the action, activity, package, or scheme assertEquals("settings", intent.getStringExtra("featureParam")); assertEquals("actions.intent.OPEN_APP_FEATURE", intent.getAction()); assertEquals("com.google.android.youtube/.MainActivity", intent.getComponent().flattenToShortString()); assertEquals("com.google.myapp", intent.getPackage()); // Developers can choose to use returned Android Intent to launch and assess the activity. Below are examples for how it will look like for Robolectric and Espresso tests. // Please note that the below part is just a possible example of how Android tests are validating Activity functionality correctness for given Android Intent. // Robolectric example: MainActivity activity = Robolectric.buildActivity(MainActivity.class,intentResult.intent).create().resume().get(); TextView title: TextView = activity.findViewById(R.id.startActivityTitle) assertEquals(title?.getText()?.toString(), "Launching…") }
Si vous utilisez Espresso, vous devez modifier le mode de lancement de l'activité en fonction des résultats AATL. Voici un exemple d'utilisation d'Espresso à l'aide de la méthode
ActivityScenario
:Kotlin
ActivityScenario.launch<MainActivity>(intentResult.intent); Espresso.onView(ViewMatchers.withId(R.id.startActivityTitle)) .check(ViewAssertions.matches(ViewMatchers.withText("Launching…")))
Java
ActivityScenario.launch<MainActivity>(intentResult.intent); Espresso.onView(ViewMatchers.withId(R.id.startActivityTitle)) .check(ViewAssertions.matches(ViewMatchers.withText("Launching…")))
Assurez-vous que les propriétés de nom et de clé dans les mappages de paramètres correspondent aux paramètres de l'intent intégré. Par exemple,
exercisePlan.forExercise.name
correspond à la documentation du paramètre dansGET_EXERCISE_PLAN
.Instanciez une instance d'API avec le paramètre de contexte Android (obtenu à partir d'
ApplicationProvider
ouInstrumentationRegistry
) :- Architecture d'application à module unique :
Kotlin
private lateinit var aatl: AppActionsTestManager @Before fun init() { val appContext = ApplicationProvider.getApplicationContext() aatl = AppActionsTestManager(appContext) }
Java
private AppActionsTestManager aatl; @Before public void init() { Context appContext = ApplicationProvider.getApplicationContext(); aatl = new AppActionsTestManager(appContext); }
- Architecture d'application à plusieurs modules :
Kotlin
private lateinit var aatl: AppActionsTestManager @Before fun init() { val appContext = ApplicationProvider.getApplicationContext() val lookupPackages = listOf("com.myapp.mainapp", "com.myapp.resources") aatl = AppActionsTestManager(appContext, lookupPackages) }
Java
private AppActionsTestManager aatl; @Before public void init() throws Exception { Context appContext = ApplicationProvider.getApplicationContext(); List<String> lookupPackages = Arrays.asList("com.myapp.mainapp","com.myapp.resources"); aatl = new AppActionsTestManager(appContext, Optional.of(lookupPackages)); }
Exécutez la méthode
fulfill
de l'API et obtenez l'objetAppActionsFulfillmentResult
.
Effectuer des assertions
Voici la méthode recommandée pour effectuer des assertions sur la bibliothèque de test des actions dans les applications :
- Effectuez l'assertion du type de traitement d'
AppActionsFulfillmentResult
. Il doit s'agir deFulfillmentType.INTENT
ouFulfillmentType.UNFULFILLED
afin de tester le comportement de l'application en cas de requêtes d'intent intégré inattendues. - Il existe deux types de traitement :
INTENT
etDEEPLINK
.- Normalement, le développeur peut différencier les traitements
INTENT
etDEEPLINK
en examinant la balise d'intent dansshortcuts.xml
qu'il traite en déclenchant la bibliothèque. - Si une balise de modèle d'URL se trouve sous la balise d'intent, cela signifie que
DEEPLINK
répond à cet intent. - Si la méthode
getData()
de l'intent de résultat renvoie un objet non nul, cela indique également le traitementDEEPLINK
. De même, sigetData
renvoienull
, cela signifie qu'il s'agit d'un traitementINTENT
.
- Normalement, le développeur peut différencier les traitements
- Dans le cas d'
INTENT
, convertissezAppActionsFulfillmentResult
enAppActionsIntentFulfillmentResult
, récupérez l'intent Android en appelant la méthodegetIntent
et effectuez l'une des opérations suivantes :- Effectuez l'assertion des champs individuels de l'intent Android.
- Effectuez l'assertion de l'URI d'un intent accessible via la méthode intent.getData.getHost.
- Dans le cas de
DEEPLINK
, convertissezAppActionsFulfillmentResult
enAppActionsIntentFulfillmentResult
(comme pour le scénarioINTENT
ci-dessus), récupérez l'intent Android en appelantgetIntent
et effectuez l'assertion de l'URL du lien profond (accessible viaintent.getData.getHost
). - Pour
INTENT
etDEEPLINK
, vous pouvez utiliser l'intent obtenu pour lancer l'activité avec le framework de test Android choisi.
Internationalisation
Si votre application inclut plusieurs paramètres régionaux, vous pouvez configurer les tests pour qu'ils exécutent une langue spécifique. Vous pouvez également modifier directement la langue :
Kotlin
import android.content.res.Configuration import java.util.Locale ... val newLocale = Locale("es") val conf = context.resources.configuration conf = Configuration(conf) conf.setLocale(newLocale)
Java
Locale newLocale = new Locale("es"); Configuration conf = context.getResources().getConfiguration(); conf = new Configuration(conf); conf.setLocale(newLocale);
Voici un exemple de test AATL configuré pour l'espagnol (ES) :
Kotlin
import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertEquals import android.content.Context import android.content.res.Configuration import androidx.test.platform.app.InstrumentationRegistry import com.google.assistant.appactions.testing.aatl.AppActionsTestManager import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentIntentResult import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentResult import com.google.assistant.appactions.testing.aatl.fulfillment.FulfillmentType import com.google.common.collect.ImmutableMap import java.util.Locale import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) class ShortcutForDifferentLocaleTest { @Before fun setUp() { val context = InstrumentationRegistry.getInstrumentation().getContext() // change the device locale to 'es' val newLocale = Locale("es") val conf = context.resources.configuration conf = Configuration(conf) conf.setLocale(newLocale) val localizedContext = context.createConfigurationContext(conf) } @Test fun shortcutForDifferentLocale_succeeds() { val aatl = AppActionsTestManager(localizedContext) val intentName = "actions.intent.GET_EXERCISE_PLAN" val intentParams = ImmutableMap.of("exercisePlan.forExercise.name", "Running") val result = aatl.fulfill(intentName, intentParams) assertThat(result.getFulfillmentType()).isEqualTo(FulfillmentType.INTENT) val intentResult = result as AppActionsFulfillmentIntentResult assertThat(intentResult.getIntent().getData().toString()) .isEqualTo("myexercise://browse?plan=running_weekly") } }
Java
import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import android.content.Context; import android.content.res.Configuration; import androidx.test.platform.app.InstrumentationRegistry; import com.google.assistant.appactions.testing.aatl.AppActionsTestManager; import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentIntentResult; import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentResult; import com.google.assistant.appactions.testing.aatl.fulfillment.FulfillmentType; import com.google.common.collect.ImmutableMap; import java.util.Locale; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @Test public void shortcutForDifferentLocale_succeeds() throws Exception { Context context = InstrumentationRegistry.getInstrumentation().getContext(); // change the device locale to 'es' Locale newLocale = new Locale("es"); Configuration conf = context.getResources().getConfiguration(); conf = new Configuration(conf); conf.setLocale(newLocale); Context localizedContext = context.createConfigurationContext(conf); AppActionsTestManager aatl = new AppActionsTestManager(localizedContext); String intentName = "actions.intent.GET_EXERCISE_PLAN"; ImmutableMap<String, String> intentParams = ImmutableMap.of("exercisePlan.forExercise.name", "Running"); AppActionsFulfillmentResult result = aatl.fulfill(intentName, intentParams); assertThat(result.getFulfillmentType()).isEqualTo(FulfillmentType.INTENT); AppActionsFulfillmentIntentResult intentResult = (AppActionsFulfillmentIntentResult) result; assertThat(intentResult.getIntent().getData().toString()) .isEqualTo("myexercise://browse?plan=running_weekly"); }
Dépannage
Si votre test d'intégration échoue de manière inattendue, vous pouvez rechercher les messages de journal AATL dans la fenêtre Logcat d'Android Studio afin de récupérer l'avertissement ou le message d'erreur. Vous pouvez également augmenter le niveau de journalisation pour capturer plus de sorties de la bibliothèque.
Limites
Voici les limites actuelles de la bibliothèque de test des actions dans les applications :
- AATL ne teste pas les fonctionnalités de compréhension du langage naturel (NLU) ni de reconnaissance vocale.
- AATL ne fonctionne pas lorsque les tests se trouvent dans des modules autres que le module d'application par défaut.
- AATL n'est compatible qu'avec Android 7.0 "Nougat" (niveau d'API 24) ou version ultérieure.