La libreria di test di Azioni app (AATL) fornisce funzionalità che consentono agli sviluppatori di testare il fulfillment in modo programmatico, automatizzando i test che normalmente vengono eseguiti utilizzando query vocali effettive o lo strumento di test Azioni app.
La libreria aiuta a garantire che la configurazione shortcut.xml
sia corretta e che la chiamata all'intent Android descritta vada a buon fine. La libreria di test di Azioni app offre un meccanismo per verificare la capacità della tua app di soddisfare determinati intent e parametri dell'Assistente Google, convertendoli in un link diretto o in un intent Android che può essere asserito e utilizzato per creare un'istanza di un'attività Android.
I test vengono eseguiti sotto forma di unità Robolectric o test strumentati nell'ambiente Android. Ciò consente agli sviluppatori di testare l'applicazione in modo esaustivo emulando il comportamento effettivo dell'app. Per testare BII, intent personalizzati o fulfillment di link diretti, è possibile utilizzare qualsiasi framework di test strumentato (UI Automator, Espresso, JUnit4, Appium, Detox, Calabash).
Se l'applicazione è multilingue, gli sviluppatori possono verificare che la funzionalità dell'applicazione funzioni correttamente in diverse lingue.
Come funziona
Per integrare la libreria di test per le azioni app nell'ambiente di test dell'app, gli sviluppatori devono creare nuovi test Robolectric o strumentati o aggiornare quelli esistenti nel modulo app
dell'app.
Il codice del test contiene le seguenti parti:
- Inizializzazione dell'istanza della libreria, nel metodo di configurazione comune o in singoli scenari di test.
- Ogni singolo test chiama il metodo
fulfill
dell'istanza della libreria per produrre il risultato della creazione dell'intent. - Successivamente, lo sviluppatore rivendica il link diretto o attiva il fulfillment dell'app, ed esegue la convalida personalizzata sullo stato dell'app.
Requisiti per la configurazione
Per utilizzare la libreria di test, è necessaria una configurazione iniziale dell'app prima di aggiungere i test all'applicazione.
Configurazione
Per utilizzare la libreria di test di Azioni app, assicurati che l'app sia configurata come segue:
- Installa il plug-in Android per Gradle (AGP)
- Includi un file
shortcuts.xml
nella cartellares/xml
nel moduloapp
. - Assicurati che
AndroidManifest.xml
includa<meta-data android:name="android.app.shortcuts" android:resource=”@xml/shortcuts” />
in:- il tag
<application>
- il tag Avvio app
<activity>
- il tag
- Posiziona l'elemento
<capability>
all'interno dell'elemento<shortcuts>
inshortcuts.xml
Aggiungi dipendenze della libreria di test di Azioni app
Aggiungi il repository Google all'elenco dei repository di progetto in
settings.gradle
:allprojects { repositories { … google() } }
Nel file del modulo dell'app
build.gradle
, aggiungi le dipendenze di AATL:androidTestImplementation 'com.google.assistant.appactions:testing:1.0.0'
Assicurati di utilizzare il numero di versione della libreria che hai scaricato.
Crea test di integrazione
Crea nuovi test in
app/src/androidTest
. Per i test Robolectric, creali inapp/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…") }
Se utilizzi Espresso, devi modificare la modalità di avvio dell'attività in base ai risultati di AATL. Ecco un esempio di Espresso con il metodo
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…")))
Fai in modo che le proprietà di nome e chiave nelle mappature dei parametri corrispondano a quelle di BII. Ad esempio,
order.orderedItem.name
corrisponde alla documentazione per il parametro inGET_ORDER
.Crea un'istanza API con il parametro Android Context (ottenuto da
ApplicationProvider
oInstrumentationRegistry
):- Architettura dell'app a modulo singolo:
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); }
- Architettura dell'app multimodulo:
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)); }
Esegui il metodo
fulfill
dell'API e recupera l'oggettoAppActionsFulfillmentResult
.
Esegui asserzioni
Il modo consigliato per dichiarare la libreria di test di Azioni app è:
- Dichiara il tipo di evasione dell'ordine
AppActionsFulfillmentResult
. Deve essereFulfillmentType.INTENT
oFulfillmentType.UNFULFILLED
per testare il comportamento dell'app in caso di richieste BII impreviste. - Sono disponibili due tipi di evasione:
INTENT
eDEEPLINK
.- Normalmente, lo sviluppatore può distinguere tra i fulfillment
INTENT
eDEEPLINK
esaminando il tag di intent inshortcuts.xml
che vengono completati attivando la libreria. - Se sotto il tag di intent è presente un tag url-template, significa che
DEEPLINK
soddisfa questo intent. - Se il metodo
getData()
dell'intent del risultato restituisce un oggetto con valore non null, questo indica anche il fulfillmentDEEPLINK
. Allo stesso modo, segetData
restituiscenull
, significa che si tratta di un fulfillmentINTENT
.
- Normalmente, lo sviluppatore può distinguere tra i fulfillment
- Per la richiesta
INTENT
, digitaAppActionsFulfillmentResult
inAppActionsIntentFulfillmentResult
, recupera l'intent Android chiamando il metodogetIntent
ed esegui una delle seguenti operazioni:- Dichiara i singoli campi di Android Intent.
- Dichiara l'URI di un intent a cui si accede tramite il metodo intent.getData.getHost.
- Per il caso
DEEPLINK
, digitaAppActionsFulfillmentResult
inAppActionsIntentFulfillmentResult
(come per lo scenarioINTENT
riportato sopra), recupera l'intent Android chiamando il metodogetIntent
e rivendica l'URL del link diretto (accessibile tramiteintent.getData.getHost
). - Sia per
INTENT
che perDEEPLINK
, puoi utilizzare l'intent risultante per lanciare l'attività con il framework di test di Android scelto.
Internazionalizzazione
Se l'app ha più impostazioni internazionali, puoi configurare i test in modo da eseguire un particolare test insufficiente per le impostazioni internazionali. In alternativa, puoi modificare direttamente le impostazioni internazionali:
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);
Ecco un esempio di test AATL configurato per le impostazioni internazionali in spagnolo (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.ORDER_MENU_ITEM" val intentParams = ImmutableMap.of("menuItem.name", "hamburguesa con queso") val result = aatl.fulfill(intentName, intentParams) assertThat(result.getFulfillmentType()).isEqualTo(FulfillmentType.INTENT) val intentResult = result as AppActionsFulfillmentIntentResult assertThat(intentResult.getIntent().getData().toString()) .isEqualTo("myfoodapp://browse?food=food_hamburger") } }
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.ORDER_MENU_ITEM"; ImmutableMap<String, String> intentParams = ImmutableMap.of("menuItem.name", "hamburguesa con queso"); AppActionsFulfillmentResult result = aatl.fulfill(intentName, intentParams); assertThat(result.getFulfillmentType()).isEqualTo(FulfillmentType.INTENT); AppActionsFulfillmentIntentResult intentResult = (AppActionsFulfillmentIntentResult) result; assertThat(intentResult.getIntent().getData().toString()) .isEqualTo("myfoodapp://browse?food=food_hamburger"); }
Risolvere i problemi
Se il test di integrazione ha esito negativo, puoi cercare i messaggi di log di AATL nella finestra logcat di Android Studio per ricevere il messaggio di avviso o di livello di errore. Puoi anche aumentare il livello di logging per acquisire più output dalla libreria.
Limitazioni
Di seguito sono riportate le attuali limitazioni della libreria di test di Azioni app :
- AATL non testa le funzionalità di comprensione del linguaggio naturale (NLU) o Speech-to-Text (STT).
- AATL non funziona quando i test si trovano in moduli diversi da quello predefinito dell'app.
- AATL è compatibile solo con Android 7.0 "Nougat" (livello API 24) e versioni successive.