Biblioteka testowa działań w aplikacji (AATL) umożliwia programistom aby automatycznie testować realizację akcji w aplikacji, zautomatyzowając testowanie, zwykle odbywać się za pomocą prawdziwych zapytań głosowych lub narzędzia do testowania akcji w aplikacji.
Biblioteka pomaga sprawdzić, czy konfiguracja shortcut.xml
jest prawidłowa i
opisane wywołanie intencji Androida kończy się powodzeniem. Biblioteka testów Akcji w aplikacji
zapewnia mechanizm testowania funkcji aplikacji, które mogą spełnić określone
zamiary i parametry Asystenta, przekształcając je w precyzyjne linki na Androida lub
intencję Androida, którą można potwierdzić i wykorzystać do utworzenia instancji Androida.
działania.
Testy są przeprowadzane w postaci testów jednostkowych Robolectric lub testów z użyciem instrumentacji w środowisku Androida. Dzięki temu deweloperzy mogą przez emulację rzeczywistego działania aplikacji. Do testowania BII takich jak realizacja precyzyjnych linków, dowolna platforma testowania (UI Automator, Espresso, JUnit4, Appium, Detox, Calabash).
Jeśli aplikacja jest wielojęzyczna, programiści mogą sprawdzić, czy działanie aplikacji działa poprawnie w różnych regionach.
Jak to działa
Aby zintegrować Bibliotekę testów akcji aplikacji z otoczeniem testowania aplikacji, deweloperzy powinni utworzyć nowe lub zaktualizować istniejące testy Robolectric lub testy z instrumentacją w module app
aplikacji.
Kod testowy zawiera te elementy:
- Inicjalizacja instancji biblioteki w typowej metodzie konfiguracji lub w dla poszczególnych przypadków testowych.
- Każdy test wywołuje metodę
fulfill
instancji biblioteki, aby nie dają wyniku tworzenia intencji. - Następnie programista potwierdzi precyzyjny link lub aktywuje realizację aplikacji, i przeprowadza niestandardową weryfikację stanu aplikacji.
Wymagania związane z konfiguracją
Aby można było korzystać z biblioteki testowej, aplikacja musi zostać wcześniej skonfigurowana wymagane przed dodaniem testów do aplikacji.
Konfiguracja
Aby korzystać z biblioteki testów działań w aplikacji, upewnij się, że aplikacja jest skonfigurowana w ten sposób:
- zainstalować wtyczkę Androida do obsługi Gradle (AGP),
- Dodaj plik
shortcuts.xml
do folderures/xml
w moduleapp
. - Upewnij się, że
AndroidManifest.xml
zawiera parametr<meta-data android:name="android.app.shortcuts" android:resource=”@xml/shortcuts” />
w ramach jednej z poniższych opcji:- tag
<application>
- tag
<activity>
programu uruchamiającego.
- tag
- Umieść element
<capability>
wewnątrz elementu<shortcuts>
wshortcuts.xml
Dodawanie zależności biblioteki testów działań aplikacji
Dodaj repozytorium Google do listy repozytoriów projektu w
settings.gradle
:allprojects { repositories { … google() } }
W pliku
build.gradle
modułu aplikacji dodaj zależności AATL:androidTestImplementation 'com.google.assistant.appactions:testing:1.0.0'
Pamiętaj, aby użyć numeru wersji pobranej biblioteki.
Tworzenie testów integracji
Utwórz nowe testy w sekcji
app/src/androidTest
. W przypadku testów Robolectric utwórz je w ramachapp/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…") }
Jeśli używasz Espresso, musisz zmienić sposób uruchamiania aktywności. na podstawie wyników AATL. Oto przykład użycia funkcji Espresso z użyciem Metoda
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…")))
Właściwości nazwy i klucza w mapowaniach parametrów muszą być zgodne z parametrami w interfejsie BII. Na przykład
exercisePlan.forExercise.name
wskazuje dopasowanie do dokumentacji parametru wGET_EXERCISE_PLAN
.Tworzenie instancji interfejsu API z parametrem Android Context (uzyskanym z
ApplicationProvider
lubInstrumentationRegistry
):- Architektura aplikacji z jednym modułem:
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); }
- Architektura aplikacji wielomodułowa:
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)); }
Wykonaj metodę
fulfill
interfejsu API i uzyskajAppActionsFulfillmentResult
obiekt.
Wykonywanie stwierdzeń
Zalecamy następujące sposoby weryfikowania Biblioteki testów działań aplikacji:
- Określ typ realizacji zamówienia
AppActionsFulfillmentResult
. Musi to byćFulfillmentType.INTENT
lubFulfillmentType.UNFULFILLED
, aby przetestować, jak aplikacja zachowuje się w przypadku nieoczekiwanych żądań BII. - Istnieją 2 rodzaje realizacji:
INTENT
iDEEPLINK
.- Normalnie deweloper może odróżnić
INTENT
odDEEPLINK
zrealizowania na podstawie tagu intencji w:shortcuts.xml
które realizują dzięki aktywacji biblioteki. - Jeśli pod tagiem intencji znajduje się tag szablonu URL, oznacza to,
że
DEEPLINK
spełnia ten zamiar. - Jeśli metoda
getData()
intencji wyniku zwraca obiekt inny niż null, oznacza to również spełnienie warunkuDEEPLINK
. Podobnie, jeśligetData
zwracanull
, co oznacza, że jest to realizacjaINTENT
.
- Normalnie deweloper może odróżnić
- W przypadku
INTENT
przypadku rzutu typuAppActionsFulfillmentResult
naAppActionsIntentFulfillmentResult
, pobierz intencję Androida za pomocą wywołaniagetIntent
i wykonaj jedną z tych czynności:- Zatwierdzanie poszczególnych pól intencji Androida.
- Potwierdzanie identyfikatora URI intencji, do której dostęp jest uzyskiwany przez intencji.getData.getHost.
- W przypadku
DEEPLINK
przypadku rzutu typuAppActionsFulfillmentResult
naAppActionsIntentFulfillmentResult
(tak samo jak w scenariuszuINTENT
powyżej), pobierz intencję Androida, wywołując metodęgetIntent
i zatwierdzając URL precyzyjnego linku (dostępny za pomocą elementuintent.getData.getHost
). - W przypadku zasobów typu
INTENT
iDEEPLINK
możesz użyć wynikowej intencji do uruchomienia na wybranych platformach testowania Androida.
Internacjonalizacja
Jeśli aplikacja ma wiele języków, możesz skonfigurować testy, aby przeprowadzić określone dla języka polskiego. Możesz też bezpośrednio zmienić lokalizację:
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);
Oto przykład testu AATL skonfigurowanego dla języka hiszpańskiego (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"); }
Rozwiązywanie problemów
Jeśli test integracji nieoczekiwanie zakończy się niepowodzeniem, możesz poszukać komunikatów z dziennika AATL w oknie dziennika Androida Studio, gdzie pojawi się ostrzeżenie lub komunikat na poziomie błędu. Można również zwiększyć , aby przechwytywać więcej danych wyjściowych do biblioteki.
Ograniczenia
Oto bieżące ograniczenia biblioteki testów działań w aplikacji :
- AATL nie testuje funkcji rozumienia języka naturalnego (NLU) ani zamiany mowy na tekst (STT).
- AATL nie działa, gdy testy znajdują się w modułach innych niż aplikacja domyślna .
- AATL jest obsługiwany tylko na Androidzie 7.0 „Nougat” (poziom interfejsu API 24) i nowszych wersjach.