Die App Actions-Testbibliothek (App Actions-Testbibliothek, AATL) bietet Entwicklern die Möglichkeit, die Auftragsausführung von App Action programmatisch zu testen. Dabei werden Tests automatisiert, die normalerweise mit echten Sprachabfragen oder dem App Actions-Testtool durchgeführt werden.
Die Bibliothek sorgt dafür, dass die shortcut.xml
-Konfiguration korrekt ist und der beschriebene Android-Intent-Aufruf erfolgreich ist. Die App Actions-Testbibliothek bietet einen Mechanismus, mit dem du testen kannst, ob deine App bestimmte Google Assistant-Intents und -Parameter erfüllen kann. Dazu konvertierst du sie in einen Android-Deeplink oder einen Android-Intent, der bestätigt und zum Instanziieren einer Android-Aktivität verwendet werden kann.
Die Tests werden in Form von Robolectric-Einheiten oder instrumentierten Tests in der Android-Umgebung durchgeführt. Dies ermöglicht Entwicklern, ihre Anwendung umfassend zu testen, indem sie das tatsächliche App-Verhalten emulieren. Zum Testen von BIIs, benutzerdefinierten Intents oder Deeplink-Auftragsausführung kann jedes instrumentierte Test-Framework verwendet werden (UI Automator, Espresso, JUnit4, Appium, Detox, Calabash).
Wenn die App mehrsprachig ist, können Entwickler überprüfen, ob die Funktionalität der Anwendung in verschiedenen Sprachen korrekt funktioniert.
So funktionierts
Zum Einbinden der App Actions-Testbibliothek in die Testumgebung der App sollten Entwickler neue Robolectric- oder instrumentierte Tests im Modul app
der App erstellen oder vorhandene aktualisieren.
Der Testcode enthält die folgenden Teile:
- Initialisierung der Bibliotheksinstanz in der üblichen Einrichtungsmethode oder in einzelnen Testläufen.
- Jeder einzelne Test ruft die Methode
fulfill
der Bibliotheksinstanz auf, um das Ergebnis der Intent-Erstellung zu generieren. - Der Entwickler bestätigt dann den Deeplink oder löst die App-Auftragsausführung aus und führt eine benutzerdefinierte Validierung des App-Status aus.
Einrichtungsanforderungen
Damit Sie die Testbibliothek verwenden können, ist eine Erstkonfiguration der Anwendung erforderlich, bevor Sie die Tests Ihrer Anwendung hinzufügen.
Konfiguration
Damit Sie die App Actions-Testbibliothek verwenden können, muss Ihre App so konfiguriert sein:
- Installieren Sie das Android Gradle-Plug-in (AGP).
- Fügen Sie die Datei
shortcuts.xml
im Ordnerres/xml
des Modulsapp
ein. - Achten Sie darauf, dass
AndroidManifest.xml
<meta-data android:name="android.app.shortcuts" android:resource=”@xml/shortcuts” />
unter folgenden Elementen enthält:- das
<application>
-Tag - das Launcher-Tag
<activity>
- das
- Platzieren Sie das
<capability>
-Element innerhalb des<shortcuts>
-Elements inshortcuts.xml
.
Abhängigkeiten der App Actions-Testbibliothek hinzufügen
Fügen Sie das Google-Repository zur Liste der Projekt-Repositories in
settings.gradle
hinzu:allprojects { repositories { … google() } }
Fügen Sie in der Datei
build.gradle
des App-Moduls die AATL-Abhängigkeiten hinzu:androidTestImplementation 'com.google.assistant.appactions:testing:1.0.0'
Verwenden Sie die Versionsnummer der heruntergeladenen Bibliothek.
Integrationstests erstellen
Erstellen Sie neue Tests unter
app/src/androidTest
. Erstellen Sie für Robolectric-Tests diese unterapp/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…") }
Wenn Sie Espresso verwenden, müssen Sie anhand der AATL-Ergebnisse ändern, wie die Aktivität gestartet wird. Hier ist ein Beispiel für Espresso mit der Methode
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…")))
Der Name und die Schlüsselattribute in den Parameterzuordnungen müssen mit den Parametern aus BII übereinstimmen.
order.orderedItem.name
entspricht beispielsweise der Dokumentation für den Parameter inGET_ORDER
.Instanziieren Sie die API-Instanz mit dem Android-Kontextparameter (über
ApplicationProvider
oderInstrumentationRegistry
abgerufen):- Anwendungsarchitektur mit einem einzelnen Modul:
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); }
- Anwendungsarchitektur mit mehreren Modulen:
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)); }
Führen Sie die Methode
fulfill
der API aus und rufen Sie das ObjektAppActionsFulfillmentResult
ab.
Assertions ausführen
Die empfohlene Methode zum Bestätigen der App Actions-Testbibliothek lautet:
- Bestätigen Sie den Auftragsausführungstyp von
AppActionsFulfillmentResult
. Er mussFulfillmentType.INTENT
oderFulfillmentType.UNFULFILLED
sein, um zu testen, wie sich die Anwendung bei unerwarteten BII-Anfragen verhält. - Es gibt zwei Arten der Auftragsausführung:
INTENT
- undDEEPLINK
-Auftragsausführungen.- Normalerweise kann der Entwickler zwischen
INTENT
- undDEEPLINK
-Auftragsausführungen unterscheiden, indem er sich das Intent-Tag inshortcuts.xml
ansieht, das er erfüllt, indem er die Bibliothek auslöst. - Wenn sich unter dem Intent-Tag ein URL-Vorlagen-Tag befindet, bedeutet das, dass
DEEPLINK
diesen Intent erfüllt. - Wenn die Methode
getData()
des Ergebnis-Intents ein Objekt zurückgibt, das nicht null ist, gibt dies auch dieDEEPLINK
-Auftragsausführung an. WenngetData
null
zurückgibt, bedeutet dies ebenfalls, dass es sich um eineINTENT
-Auftragsausführung handelt.
- Normalerweise kann der Entwickler zwischen
- Wandeln Sie für den
INTENT
-FallAppActionsFulfillmentResult
inAppActionsIntentFulfillmentResult
um, rufen Sie den Android-Intent durch Aufrufen der MethodegetIntent
ab und führen Sie einen der folgenden Schritte aus:- Einzelne Felder des Android-Intents erzwingen.
- Bestätigen Sie den URI eines Intents, auf den über die Methode „intent.getData.getHost“ zugegriffen wird.
- Wandeln Sie im
DEEPLINK
-FallAppActionsFulfillmentResult
inAppActionsIntentFulfillmentResult
um (wie für dasINTENT
-Szenario oben), rufen Sie den Android-Intent ab, indem Sie die MethodegetIntent
aufrufen und die Deeplink-URL bestätigen (Zugriff überintent.getData.getHost
). - Sowohl für
INTENT
als auch fürDEEPLINK
können Sie den resultierenden Intent verwenden, um die Aktivität mit dem ausgewählten Android-Test-Framework zu starten.
Lokalisierung
Wenn Ihre Anwendung mehrere Sprachen hat, können Sie Tests so konfigurieren, dass eine bestimmte zu testende Sprache ausgeführt wird. Alternativ können Sie das Gebietsschema direkt ändern:
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);
Hier ein Beispiel für einen AATL-Test, der für die Sprache Spanisch (ES) konfiguriert ist:
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"); }
Fehlerbehebung
Wenn Ihr Integrationstest unerwartet fehlschlägt, können Sie im Logcat-Fenster von Android Studio nach AATL-Logmeldungen suchen, um die Warn- oder Fehlermeldung zu erhalten. Sie können auch die Logging-Ebene erhöhen, um mehr Ausgabedaten aus der Bibliothek zu erfassen.
Einschränkungen
Im Folgenden sind die aktuellen Einschränkungen der App Actions-Testbibliothek aufgeführt :
- AATL testet keine Features von Natural Language Understanding (NLU) oder Speech-to-Text (STT).
- AATL funktioniert nicht, wenn sich Tests in anderen Modulen als dem Standard-App-Modul befinden.
- AATL ist nur mit Android 7.0 „Nougat“ (API-Level 24) und höher kompatibel.