La libreria di test App Actions (AATL) fornisce funzionalità che consentono agli sviluppatori di testare in modo programmatico l'aggiornamento delle azioni dell'app, automatizzando i test che normalmente vengono eseguiti utilizzando query vocali effettive o lo strumento di test App Actions.
La raccolta contribuisce ad assicurare che la configurazione di shortcut.xml
sia corretta e che l'invocazione dell'intent Android descritta vada a buon fine. La libreria di test di App Actions
fornisce un meccanismo per verificare la capacità della tua app di soddisfare determinati intent e parametri dell'Assistente Google, convertendoli in un link diretto Android o in un intent Android, che può essere verificato e utilizzato per creare un'attività Android.
I test vengono eseguiti sotto forma di test delle unità o test con strumenti Robolectric nell'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 il comportamento della funzionalità dell'applicazione è corretto in diverse impostazioni internazionali.
Come funziona
Per integrare la libreria di test delle Azioni app nell'ambiente di test dell'app, gli sviluppatori devono
crea nuovi test robot o strumentati o aggiorna quelli esistenti su app
dell'app.
Il codice di test è costituito dalle seguenti parti:
- Inizializzazione dell'istanza della libreria, nel metodo di configurazione comune o nei singoli casi di test.
- Ogni singolo test chiama il metodo
fulfill
dell'istanza della libreria per produrre il risultato della creazione dell'intent. - Lo sviluppatore verifica il link diretto o attiva l'app fulfillment esegue la convalida personalizzata dello 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 App Actions, assicurati che la tua app sia configurata come segue:
- Installa il plug-in Android per Gradle (AGP)
- Includi un file
shortcuts.xml
nella cartellares/xml
del 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 delle Azioni app
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 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 il modo in cui avvii l'attività in base ai risultati AATL. Ecco un esempio per Espresso che utilizza 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…")))
Le proprietà name e key nelle mappature dei parametri corrispondano ai parametri del BII. Ad esempio,
exercisePlan.forExercise.name
corrisponde alla documentazione per il parametro inGET_EXERCISE_PLAN
.Istanza l'istanza dell'API con il parametro Android Context (ottenuto da
ApplicationProvider
oInstrumentationRegistry
):- Architettura dell'app a un modulo:
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 multi-modulo:
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
. Deve essereFulfillmentType.INTENT
oFulfillmentType.UNFULFILLED
per testare il comportamento dell'app in caso di richieste di intent integrati inaspettate. - Esistono due tipi di esecuzione:
INTENT
eDEEPLINK
.- In genere, lo sviluppatore può distinguere tra i completamenti
INTENT
eDEEPLINK
osservando il tag intent inshortcuts.xml
che sta completando attivando la libreria. - 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, anche questo indicaDEEPLINK
il completamento. Analogamente, segetData
restituiscenull
significa che si tratta di un adempimentoINTENT
.
- In genere, lo sviluppatore può distinguere tra i completamenti
- Per il caso
INTENT
, digitaAppActionsFulfillmentResult
inAppActionsIntentFulfillmentResult
, recupera l'intent Android richiamandogetIntent
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);
Ecco 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.