La libreria di test delle Azioni app (AATL) offre funzionalità per consentire agli sviluppatori per testare il completamento delle Azioni app in modo programmatico, automatizzando i test che di solito vengono eseguite utilizzando query vocali reali o lo strumento di test delle Azioni app.
La libreria contribuisce a garantire che la configurazione di shortcut.xml
sia corretta e
la chiamata dell'intent Android descritta ha esito positivo. Libreria di test delle Azioni app
offre un meccanismo per testare la capacità dell'app di soddisfare i requisiti di Google,
Intent e parametri dell'assistente, convertendoli in un link diretto Android oppure
Per intent Android, che può essere rivendicato e utilizzato per creare un'istanza di un Android
attività.
I test vengono eseguiti sotto forma di unità robotica o test strumentati in dell'ambiente Android. In questo modo, gli sviluppatori possono testare in modo completo la loro applicazione emulando il comportamento effettivo dell'app. Per testare gli intent integrati, intent o completamento di link diretti, qualsiasi framework di test strumentato può utilizzato (UI Automator, Espresso, JUnit4, Appium, Detox, Calabash).
Se l'applicazione è multilingue, gli sviluppatori possono verificare che la funzionalità dell'applicazione funzioni correttamente in diversi paesi.
Come funziona
Per integrare la libreria di test delle Azioni app nell'ambiente di test dell'app, gli sviluppatori devono
crea nuovi test robotless o strumentati o aggiorna quelli esistenti su app
dell'app.
Il codice di 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 a per produrre il risultato della creazione dell'intent. - Lo sviluppatore quindi dichiara il link diretto o attiva il completamento dell'app. ed esegue una convalida personalizzata per lo stato dell'app.
Requisiti per la configurazione
Per poter utilizzare la libreria di test, sono necessarie alcune configurazioni iniziali dell'app richiesta prima di aggiungere i test all'applicazione.
Configurazione
Per utilizzare la Raccolta di test delle Azioni app, assicurati che l'app sia configurata come segue:
- Installa il plug-in Android 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 le dipendenze della libreria di test di App Actions
Aggiungi il repository Google all'elenco dei repository del progetto in
settings.gradle
:allprojects { repositories { … google() } }
Nel file
build.gradle
del modulo dell'app, aggiungi le dipendenze 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 robotici, crea sottoapp/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 usi Espresso, devi modificare la modalità di avvio dell'Attività. in base ai risultati di AATL. Ecco un esempio per Espresso in cui viene utilizzata la 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 il nome e le proprietà della chiave nelle mappature dei parametri corrispondano al dall'intent integrato. Ad esempio,
exercisePlan.forExercise.name
corrisponde alla documentazione per il parametro inGET_EXERCISE_PLAN
.Creare un'istanza dell'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 delle 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 ottieni OggettoAppActionsFulfillmentResult
.
Eseguire asserzioni
Il modo consigliato per dichiarare la libreria di test delle Azioni app è:
- Verifica il tipo di evasione dell'
AppActionsFulfillmentResult
. DeveFulfillmentType.INTENT
oFulfillmentType.UNFULFILLED
per testare il comportamento dell'app in caso di richieste di intent integrati impreviste. - Esistono due tipi di esecuzione:
INTENT
eDEEPLINK
.- Di solito, lo sviluppatore è in grado di distinguere tra
INTENT
eDEEPLINK
completamenti esaminando il tag di intent inshortcuts.xml
che completano attivando la raccolta. - Se è presente un tag url-template sotto il tag per intent, questo indica
che
DEEPLINK
soddisfi questo intento. - Se il metodo
getData()
dell'intent del risultato restituisce un oggetto non nullo, questo indica anche il completamento diDEEPLINK
. Allo stesso modo, segetData
restituiscenull
, significa che si tratta di un fulfillmentINTENT
.
- Di solito, lo sviluppatore è in grado di distinguere tra
- Per il caso
INTENT
, digitaAppActionsFulfillmentResult
inAppActionsIntentFulfillmentResult
, recupera l'intent Android chiamandogetIntent
ed esegui una delle seguenti operazioni:- Dichiarare i singoli campi di Android Intent.
- Verifica 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
sopra), recupera l'intent Android richiamando il metodogetIntent
e dichiara L'URL del link diretto (accessibile tramiteintent.getData.getHost
). - Sia per
INTENT
che perDEEPLINK
, puoi utilizzare l'intent risultante per avviare l'attività con il framework di test Android scelto.
Internazionalizzazione
Se la tua app ha più lingue, puoi configurare i test in modo da eseguire un determinato linguaggio sottoposto a test. 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);
Di seguito è riportato un esempio di test AATL configurato per le impostazioni internazionali spagnole (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"); }
Risoluzione dei problemi
Se il test di integrazione non riesce in modo imprevisto, puoi cercare i messaggi di log AATL nella finestra di logcat di Android Studio per ricevere il messaggio di avviso o di errore. Puoi anche aumentare il logging per acquisire più output nella libreria.
Limitazioni
Queste sono le limitazioni attuali della libreria di test delle Azioni app :
- AATL non testa la comprensione del linguaggio naturale (NLU) o la conversione della voce in testo (STT).
- AATL non funziona quando i test sono in moduli diversi dall'app predefinita in maggior dettaglio più avanti in questo modulo.
- AATL è compatibile solo con Android 7.0 "Nougat" (livello API 24) e più recenti.