Mit der App Actions Test Library (AATL) können Entwickler die App-Aktion-Ausführung programmatisch testen und so Tests automatisieren, die normalerweise mit echten Sprachanfragen oder dem App Actions-Testtool durchgeführt werden.
Die Bibliothek sorgt dafür, dass die shortcut.xml
-Konfiguration korrekt und
der beschriebene Android-Intent-Aufruf erfolgreich ist. App Actions-Testbibliothek
einen Mechanismus bietet, mit dem Sie testen können, ob Ihre App
Assistant-Intent und -Parameter, indem sie in einen Android-Deeplink umgewandelt oder
Android-Intent, der geltend gemacht und zur Instanziierung eines Android-Geräts verwendet werden kann.
Aktivitäten.
Die Tests werden in Form von Robolectric-Einheits- oder instrumentierten Tests in der Android-Umgebung durchgeführt. So können Entwickelnde ihre indem Sie das tatsächliche App-Verhalten emulieren. Zum Testen von BIIs, benutzerdefiniert Intents oder Deeplink-Fulfillment nutzen, kann jedes instrumentierte Test-Framework verwendet (UI Automator, Espresso, JUnit4, Appium, Detox, Calabash).
Wenn die Anwendung mehrsprachig ist, können Entwickler prüfen, ob die Funktionen der Anwendung in verschiedenen Sprachen korrekt funktionieren.
Funktionsweise
Wenn Entwickler die App Actions Test Library in die Testumgebung der App einbinden möchten, sollten sie neue Robolectric- oder instrumentierte Tests im app
-Modul der App erstellen oder vorhandene aktualisieren.
Der Testcode enthält die folgenden Teile:
- Initialisierung der Bibliotheksinstanz in der gemeinsamen Einrichtungsmethode oder in einzelne Testfälle.
- Bei jedem einzelnen Test wird die
fulfill
-Methode der Bibliotheksinstanz aufgerufen, um das Ergebnis der Intent-Erstellung zu erhalten. - Der Entwickler bestätigt dann den Deeplink oder löst die App-Ausführung aus und führt eine benutzerdefinierte Validierung des App-Status durch.
Voraussetzungen für die Einrichtung
Damit Sie die Testbibliothek verwenden können, ist eine anfängliche App-Konfiguration erforderlich, bevor Sie die Tests Ihrer App 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üge im Modul
app
im Ordnerres/xml
eine Datei „shortcuts.xml
“ hinzu. - Achten Sie darauf, dass
AndroidManifest.xml
<meta-data android:name="android.app.shortcuts" android:resource=”@xml/shortcuts” />
enthält:- das
<application>
-Tag - das Launcher-
<activity>
-Tag
- das
- Platzieren Sie das
<capability>
-Element innerhalb des<shortcuts>
-Elements inshortcuts.xml
Abhängigkeiten der App Actions-Testbibliothek hinzufügen
Google-Repository zur Liste der Projekt-Repositories in hinzufügen
settings.gradle
: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 sie 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 die Ausführung der Aktivität anhand der AATL-Ergebnisse anpassen. Hier ist ein Beispiel für Espresso mit der Methode
ActivityScenario
-Methode: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…")))
Die Namen und Schlüsseleigenschaften in den Parameterzuordnungen müssen mit den Parametern aus dem BII übereinstimmen. Beispiel:
exercisePlan.forExercise.name
stimmt mit der Dokumentation für den Parameter inGET_EXERCISE_PLAN
überein.API-Instanz mit dem Android-Kontextparameter erstellen (aus
ApplicationProvider
oderInstrumentationRegistry
abgerufen):- Anwendungsarchitektur mit einem 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); }
- Architektur von Anwendungen 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 dasAppActionsFulfillmentResult
-Objekt ab.
Behauptungen prüfen
Die empfohlene Methode zum Bestätigen der App Actions-Testbibliothek ist:
- Geben Sie den Auftragsausführungstyp der
AppActionsFulfillmentResult
an. Es mussFulfillmentType.INTENT
oderFulfillmentType.UNFULFILLED
sein, um Sie können testen, wie sich die App bei unerwarteten BII-Anfragen verhält. - Es gibt zwei Arten der Auftragsausführung:
INTENT
undDEEPLINK
.- Normalerweise kann der Entwickler zwischen
INTENT
undDEEPLINK
Ausführungen anhand des Intent-Tags inshortcuts.xml
die sie erfüllen, indem sie die Bibliothek auslösen. - Wenn sich unter dem Intent-Tag ein „url-template“-Tag befindet, erfüllt die
DEEPLINK
diesen Intent. - Wenn die
getData()
-Methode der Ergebnisabsicht ein nicht nullwertiges Objekt zurückgibt, weist dies auch auf eineDEEPLINK
-Erfüllung hin. WenngetData
den Wertnull
zurückgibt, bedeutet das, dass es sich um eineINTENT
-Ausführung handelt.
- Normalerweise kann der Entwickler zwischen
- Für den Fall
INTENT
: Führe einen Typumwandlung vonAppActionsFulfillmentResult
inAppActionsIntentFulfillmentResult
durch, hole den Android-Intent durch Aufrufen der MethodegetIntent
ab und führe einen der folgenden Schritte aus:- Einzelne Felder des Android-Intents angeben
- URI eines Intents bestätigen, auf den über intent.getData.getHost-Methode.
- Für den Fall
DEEPLINK
AppActionsFulfillmentResult
inAppActionsIntentFulfillmentResult
umwandeln (wie oben für dasINTENT
-Szenario), Android-Intent durch Aufrufen dergetIntent
-Methode abrufen und Deeplink-URL prüfen (überintent.getData.getHost
darauf zugreifen). - Sowohl für
INTENT
als auch fürDEEPLINK
können Sie die resultierende Intent-Anfrage verwenden, um die Aktivität mit dem ausgewählten Android-Testframework zu starten.
Lokalisierung
Wenn Ihre App mehrere Sprachen unterstützt, können Sie Tests so konfigurieren, dass ein bestimmter Test für eine bestimmte 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.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"); }
Fehlerbehebung
Wenn Ihr Integrationstest unerwartet fehlschlägt, können Sie nach AATL-Logmeldungen suchen im Android Studio-Logcat-Fenster ein, um die Warnung oder Fehlermeldung zu erhalten. Sie können auch den Logging-Level erhöhen, um mehr Ausgabe aus der Bibliothek zu erfassen.
Beschränkungen
Dies sind die aktuellen Einschränkungen der App Actions Test Library:
- AATL testet keine Natural Language Understanding (NLU)- oder Spracherkennungsfunktionen (Speech-to-Text, STT).
- AATL funktioniert nicht, wenn sich Tests in Modulen außerhalb der Standard-App befinden -Modul.
- AATL ist nur mit Android 7.0 „Nougat“ kompatibel (API-Level 24) und neuer.