Тестовая библиотека действий в приложении (AATL) позволяет разработчикам программно тестировать выполнение действий в приложении, автоматизируя тестирование, которое обычно выполняется с помощью реальных голосовых запросов или инструмента тестирования действий в приложении.
Библиотека помогает убедиться, что конфигурация shortcut.xml задана правильно и вызов описанного намерения Android выполняется успешно. Тестовая библиотека действий в приложении позволяет проверить, может ли ваше приложение выполнять заданные намерения и параметры Google Ассистента. Для этого они преобразуются в ссылку на контент в Android или намерение Android, которые можно подтвердить и использовать для создания экземпляра действия Android.
Тестирование выполняется в виде модульных или инструментальных тестов Robolectric в среде Android. Это позволяет разработчикам всесторонне протестировать свое приложение, эмулируя его фактическое поведение. Для тестирования встроенных и пользовательских намерений, а также выполнения ссылок на контент можно использовать любой фреймворк для инструментального тестирования (UI Automator, Espresso, JUnit4, Appium, Detox, Calabash).
Если приложение многоязычное, разработчики могут проверить, правильно ли работают его функции в разных регионах.
Принцип работы
Чтобы интегрировать тестовую библиотеку действий в приложении в тестовую среду приложения, разработчикам нужно создать новые или обновить существующие тесты Robolectric или инструментальные тесты в модуле app приложения.
Код теста состоит из следующих частей:
- Инициализация экземпляра библиотеки в общем методе настройки или в отдельных тестовых примерах.
- Каждый отдельный тест вызывает метод
fulfillэкземпляра библиотеки, чтобы получить результат создания намерения. - Затем разработчик подтверждает ссылку на контент или запускает выполнение в приложении и запускает собственную валидацию состояния приложения.
Требования по настройке
Чтобы использовать тестовую библиотеку, необходимо выполнить начальную настройку приложения, прежде чем добавлять в него тесты.
Конфигурация
Чтобы использовать библиотеку тестирования действий в приложении, убедитесь, что ваше приложение настроено следующим образом:
- Установите плагин Android Gradle (AGP).
- Добавьте файл
shortcuts.xmlв папкуres/xmlв модулеapp. - Убедитесь, что
AndroidManifest.xmlвключает<meta-data android:name="android.app.shortcuts" android:resource=”@xml/shortcuts” />в одном из следующих разделов:- тег
<application> - тег
<activity>;
- тег
- Разместите элемент
<capability>внутри элемента<shortcuts>вshortcuts.xml.
Добавьте зависимости тестовой библиотеки действий в приложении
Добавьте репозиторий Google в список репозиториев проекта в файле
settings.gradle:allprojects { repositories { … google() } }В файл
build.gradleмодуля приложения добавьте зависимости AATL:androidTestImplementation 'com.google.assistant.appactions:testing:1.0.0'Убедитесь, что вы используете номер версии скачанной библиотеки.
Создайте интеграционные тесты
Создайте новые тесты в разделе
app/src/androidTest. Для тестов Robolectric создайте их в каталогеapp/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…") }
Если вы используете Espresso, вам нужно изменить способ запуска Activity на основе результатов AATL. Ниже приведен пример для Espresso с использованием метода
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…")))
Убедитесь, что названия и ключевые свойства в сопоставлениях параметров соответствуют параметрам из BII. Например,
exercisePlan.forExercise.nameсоответствует документации для параметра вGET_EXERCISE_PLAN.Создайте экземпляр API с параметром Android Context (полученным из
ApplicationProviderилиInstrumentationRegistry):- Архитектура приложения с одним модулем:
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); }
- Многомодульная архитектура приложения:
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)); }
Выполните метод
fulfillAPI и получите объектAppActionsFulfillmentResult.
Выполнение утверждений
Рекомендуемый способ проверки библиотеки тестирования действий в приложении:
- Укажите тип выполнения заказа для
AppActionsFulfillmentResult. Чтобы проверить, как приложение ведет себя в случае непредвиденных запросов встроенных намерений, необходимо указатьFulfillmentType.INTENTилиFulfillmentType.UNFULFILLED. - Существует два типа выполнения:
INTENTиDEEPLINK.- Обычно разработчик может отличить выполнение
INTENTот выполненияDEEPLINK, посмотрев на тег намерения вshortcuts.xml, который он выполняет, запуская библиотеку. - Если под тегом intent есть тег url-template, это означает, что
DEEPLINKвыполняет это намерение. - Если метод
getData()намерения результата возвращает ненулевой объект, это также указывает на выполнениеDEEPLINK. Аналогично, еслиgetDataвозвращаетnull, это означает, что заказ выполняетсяINTENT.
- Обычно разработчик может отличить выполнение
- Для
INTENTприведитеAppActionsFulfillmentResultкAppActionsIntentFulfillmentResult, получите Android Intent, вызвав методgetIntent, и выполните одно из следующих действий:- Проверьте отдельные поля Android Intent.
- Проверьте URI намерения, доступ к которому получен с помощью метода intent.getData.getHost.
- Для случая
DEEPLINKприведите типAppActionsFulfillmentResultкAppActionsIntentFulfillmentResult(как в сценарииINTENTвыше), получите намерение Android, вызвав методgetIntent, и подтвердите URL ссылки на контент (доступный черезintent.getData.getHost). - В обоих случаях (
INTENTиDEEPLINK) вы можете использовать полученный объект Intent, чтобы запустить действие с помощью выбранного фреймворка для тестирования Android.
Языковые настройки
Если ваше приложение поддерживает несколько языков, вы можете настроить тесты для запуска определенного языка. Вы также можете изменить региональные настройки напрямую:
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);
Ниже приведен пример теста AATL, настроенного для испанского языка (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"); }
Устранение неполадок
Если интеграционный тест неожиданно завершился сбоем, вы можете найти сообщения в журнале AATL в окне logcat Android Studio, чтобы получить сообщение об уровне предупреждения или ошибки. Вы также можете повысить уровень ведения журнала, чтобы получать больше выходных данных из библиотеки.
Ограничения
Текущие ограничения тестовой библиотеки действий в приложениях :
- AATL не тестирует функции понимания естественного языка (NLU) и преобразования речи в текст (STT).
- AATL не работает, если тесты находятся в модулях, отличных от модуля приложения по умолчанию.
- AATL совместима только с Android 7.0 Nougat (уровень API 24) и более поздними версиями.