Библиотека тестов действий приложений

Библиотека тестирования действий приложения (AATL) предоставляет разработчикам возможность программно тестировать выполнение действий приложения, автоматизируя тестирование, которое обычно выполняется с использованием реальных голосовых запросов или инструмента тестирования действий приложения.

Библиотека помогает убедиться в правильности конфигурации shortcut.xml и успешности описанного вызова намерения Android. Библиотека тестирования действий приложения предоставляет механизм для проверки способности вашего приложения выполнять заданные намерения и параметры Google Ассистента путем преобразования их в глубокую ссылку Android или намерение Android, которые можно утверждать и использовать для создания экземпляра действия Android.

Тестирование проводится в виде модульных Robolectric или инструментальных тестов в среде Android. Это позволяет разработчикам всесторонне тестировать свое приложение, эмулируя фактическое поведение приложения. Для тестирования BII, пользовательских намерений или выполнения глубоких ссылок можно использовать любую инструментальную среду тестирования (UI Automator, Espresso, JUnit4, Appium, Detox, Calabash).

Если приложение является многоязычным, разработчики могут проверить правильность работы функций приложения в разных языковых стандартах.

Как это работает

Чтобы интегрировать библиотеку тестов App Actions в среду тестирования приложения, разработчикам следует создать новые или обновить существующие 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

Добавление зависимостей библиотеки тестов App Actions

  1. Добавьте репозиторий Google в список репозиториев проекта в settings.gradle :

        allprojects {
            repositories {
                
                google()
            }
        }
    
  2. В файле build.gradle модуля приложения добавьте зависимости AATL:

        androidTestImplementation 'com.google.assistant.appactions:testing:1.0.0'
    

    Обязательно используйте номер версии загруженной вами библиотеки.

Создание интеграционных тестов

  1. Создайте новые тесты в app/src/androidTest . Для тестов Robolectric создайте их в app/src/test :

    Котлин

      
        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…")
        }
      

    Ява

      
        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, вам необходимо изменить способ запуска действия на основе результатов AATL. Вот пример для Espresso с использованием метода ActivityScenario :

    Котлин

        
        ActivityScenario.launch<MainActivity>(intentResult.intent);
        Espresso.onView(ViewMatchers.withId(R.id.startActivityTitle))
          .check(ViewAssertions.matches(ViewMatchers.withText("Launching…")))
        

    Ява

        
          ActivityScenario.launch<MainActivity>(intentResult.intent);
          Espresso.onView(ViewMatchers.withId(R.id.startActivityTitle))
            .check(ViewAssertions.matches(ViewMatchers.withText("Launching…")))
        
  2. Убедитесь, что свойства имени и ключа в сопоставлениях параметров соответствуют параметрам из BII. Например, exercisePlan.forExercise.name соответствует документации для параметра в GET_EXERCISE_PLAN .

  3. Создайте экземпляр API с параметром контекста Android (полученным из ApplicationProvider или InstrumentationRegistry ):

    • Архитектура приложения с одним модулем:

    Котлин

        
          private lateinit var aatl: AppActionsTestManager
          @Before
          fun init() {
            val appContext = ApplicationProvider.getApplicationContext()
            aatl = AppActionsTestManager(appContext)
          }
        
      

    Ява

        
          private AppActionsTestManager aatl;
    
          @Before
          public void init() {
            Context appContext = ApplicationProvider.getApplicationContext();
            aatl = new AppActionsTestManager(appContext);
          }
        
      
    • Многомодульная архитектура приложения:

    Котлин

        
          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)
          }
        
      

    Ява

        
          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));
          }
        
      
  4. Выполните метод fulfill API и получите объект AppActionsFulfillmentResult .

Выполнение утверждений

Рекомендуемый способ утверждения библиотеки тестов действий приложения:

  1. Укажите тип выполнения AppActionsFulfillmentResult . Это должен быть FulfillmentType.INTENT или FulfillmentType.UNFULFILLED , чтобы проверить, как приложение ведет себя в случае неожиданных запросов BII.
  2. Существует два варианта выполнения: выполнение INTENT и DEEPLINK .
    • Обычно разработчик может различать выполнение INTENT и DEEPLINK , просматривая тег намерения в shortcuts.xml , который они выполняют, запуская библиотеку.
    • Если под тегом намерения есть тег шаблона URL-адреса, это означает, что DEEPLINK выполняет это намерение.
    • Если метод getData() результирующего намерения возвращает ненулевой объект, это также указывает на выполнение DEEPLINK . Аналогично, если getData возвращает null это означает, что это выполнение INTENT .
  3. В случае INTENT введите AppActionsFulfillmentResult в AppActionsIntentFulfillmentResult , получите намерение Android, вызвав метод getIntent , и выполните одно из следующих действий:
    • Утвердите отдельные поля Android Intent.
    • Укажите uri намерения, доступ к которому осуществляется с помощью метода Intent.getData.getHost.
  4. В случае DEEPLINK выполните приведение AppActionsFulfillmentResult к AppActionsIntentFulfillmentResult (так же, как для сценария INTENT выше), получите намерение Android, вызвав метод getIntent , и утвердите URL-адрес глубокой ссылки (доступ к которому осуществляется через intent.getData.getHost ).
  5. Как для INTENT , так и DEEPLINK вы можете использовать полученное намерение для запуска активности с выбранной платформой тестирования Android.

Интернационализация

Если ваше приложение имеет несколько языковых стандартов, вы можете настроить тесты для запуска определенного тестируемого языкового стандарта. Альтернативно вы можете напрямую изменить локаль:

Котлин

    
    import android.content.res.Configuration
    import java.util.Locale
    ...
    val newLocale = Locale("es")
    val conf = context.resources.configuration
    conf = Configuration(conf)
    conf.setLocale(newLocale)
    
  

Ява

    
    Locale newLocale = new Locale("es");
    Configuration conf = context.getResources().getConfiguration();
    conf = new Configuration(conf);
    conf.setLocale(newLocale);
    
  

Вот пример теста AATL, настроенного для испанского языка (ES):

Котлин

      
      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")
        }
      }
      
    

Ява

      
      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) и новее.
,

Библиотека тестирования действий приложения (AATL) предоставляет разработчикам возможность программно тестировать выполнение действий приложения, автоматизируя тестирование, которое обычно выполняется с использованием реальных голосовых запросов или инструмента тестирования действий приложения.

Библиотека помогает убедиться в правильности конфигурации shortcut.xml и успешности описанного вызова намерения Android. Библиотека тестирования действий приложения предоставляет механизм для проверки способности вашего приложения выполнять заданные намерения и параметры Google Ассистента путем преобразования их в глубокую ссылку Android или намерение Android, которые можно утверждать и использовать для создания экземпляра действия Android.

Тестирование проводится в виде модульных Robolectric или инструментальных тестов в среде Android. Это позволяет разработчикам всесторонне тестировать свое приложение, эмулируя фактическое поведение приложения. Для тестирования BII, пользовательских намерений или выполнения глубоких ссылок можно использовать любую инструментальную среду тестирования (UI Automator, Espresso, JUnit4, Appium, Detox, Calabash).

Если приложение является многоязычным, разработчики могут проверить правильность работы функций приложения в разных языковых стандартах.

Как это работает

Чтобы интегрировать библиотеку тестов App Actions в среду тестирования приложения, разработчикам следует создать новые или обновить существующие 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

Добавление зависимостей библиотеки тестов App Actions

  1. Добавьте репозиторий Google в список репозиториев проекта в settings.gradle :

        allprojects {
            repositories {
                
                google()
            }
        }
    
  2. В файле build.gradle модуля приложения добавьте зависимости AATL:

        androidTestImplementation 'com.google.assistant.appactions:testing:1.0.0'
    

    Обязательно используйте номер версии загруженной вами библиотеки.

Создание интеграционных тестов

  1. Создайте новые тесты в app/src/androidTest . Для тестов Robolectric создайте их в app/src/test :

    Котлин

      
        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…")
        }
      

    Ява

      
        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, вам необходимо изменить способ запуска действия на основе результатов AATL. Вот пример для Espresso с использованием метода ActivityScenario :

    Котлин

        
        ActivityScenario.launch<MainActivity>(intentResult.intent);
        Espresso.onView(ViewMatchers.withId(R.id.startActivityTitle))
          .check(ViewAssertions.matches(ViewMatchers.withText("Launching…")))
        

    Ява

        
          ActivityScenario.launch<MainActivity>(intentResult.intent);
          Espresso.onView(ViewMatchers.withId(R.id.startActivityTitle))
            .check(ViewAssertions.matches(ViewMatchers.withText("Launching…")))
        
  2. Убедитесь, что свойства имени и ключа в сопоставлениях параметров соответствуют параметрам из BII. Например, exercisePlan.forExercise.name соответствует документации для параметра в GET_EXERCISE_PLAN .

  3. Создайте экземпляр API с параметром контекста Android (полученным из ApplicationProvider или InstrumentationRegistry ):

    • Архитектура приложения с одним модулем:

    Котлин

        
          private lateinit var aatl: AppActionsTestManager
          @Before
          fun init() {
            val appContext = ApplicationProvider.getApplicationContext()
            aatl = AppActionsTestManager(appContext)
          }
        
      

    Ява

        
          private AppActionsTestManager aatl;
    
          @Before
          public void init() {
            Context appContext = ApplicationProvider.getApplicationContext();
            aatl = new AppActionsTestManager(appContext);
          }
        
      
    • Многомодульная архитектура приложения:

    Котлин

        
          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)
          }
        
      

    Ява

        
          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));
          }
        
      
  4. fulfill метод API API и получите объект AppActionsFulfillmentResult .

Выполнение утверждений

Рекомендуемый способ утверждения библиотеки тестов действий приложения:

  1. Укажите тип выполнения AppActionsFulfillmentResult . Это должен быть FulfillmentType.INTENT или FulfillmentType.UNFULFILLED , чтобы проверить, как приложение ведет себя в случае неожиданных запросов BII.
  2. Существует два варианта выполнения: выполнение INTENT и DEEPLINK .
    • Обычно разработчик может различать выполнение INTENT и DEEPLINK , просматривая тег намерения в shortcuts.xml , который они выполняют, запуская библиотеку.
    • Если под тегом намерения есть тег шаблона URL-адреса, это означает, что DEEPLINK выполняет это намерение.
    • Если метод getData() результирующего намерения возвращает ненулевой объект, это также указывает на выполнение DEEPLINK . Аналогично, если getData возвращает null это означает, что это выполнение INTENT .
  3. В случае INTENT введите AppActionsFulfillmentResult в AppActionsIntentFulfillmentResult , получите намерение Android, вызвав метод getIntent , и выполните одно из следующих действий:
    • Утвердите отдельные поля Android Intent.
    • Укажите uri намерения, доступ к которому осуществляется с помощью метода Intent.getData.getHost.
  4. В случае DEEPLINK введите AppActionsFulfillmentResult в AppActionsIntentFulfillmentResult (так же, как и для сценария INTENT выше), получите намерение Android, вызвав метод getIntent , и утвердите URL-адрес глубокой ссылки (доступ к которому осуществляется через intent.getData.getHost ).
  5. Как для INTENT , так и DEEPLINK вы можете использовать полученное намерение для запуска активности с выбранной платформой тестирования Android.

Интернационализация

Если ваше приложение имеет несколько языковых стандартов, вы можете настроить тесты для запуска определенного тестируемого языкового стандарта. Альтернативно вы можете напрямую изменить локаль:

Котлин

    
    import android.content.res.Configuration
    import java.util.Locale
    ...
    val newLocale = Locale("es")
    val conf = context.resources.configuration
    conf = Configuration(conf)
    conf.setLocale(newLocale)
    
  

Ява

    
    Locale newLocale = new Locale("es");
    Configuration conf = context.getResources().getConfiguration();
    conf = new Configuration(conf);
    conf.setLocale(newLocale);
    
  

Вот пример теста AATL, настроенного для испанского языка (ES):

Котлин

      
      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")
        }
      }
      
    

Ява

      
      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) и новее.
,

Библиотека тестирования действий приложения (AATL) предоставляет разработчикам возможность программно тестировать выполнение действий приложения, автоматизируя тестирование, которое обычно выполняется с использованием реальных голосовых запросов или инструмента тестирования действий приложения.

Библиотека помогает убедиться в правильности конфигурации shortcut.xml и успешности описанного вызова намерения Android. Библиотека тестирования действий приложения предоставляет механизм для проверки способности вашего приложения выполнять заданные намерения и параметры Google Ассистента путем преобразования их в глубокую ссылку Android или намерение Android, которые можно утверждать и использовать для создания экземпляра действия Android.

Тестирование проводится в виде модульных Robolectric или инструментальных тестов в среде Android. Это позволяет разработчикам всесторонне тестировать свое приложение, эмулируя фактическое поведение приложения. Для тестирования BII, пользовательских намерений или выполнения глубоких ссылок можно использовать любую инструментальную среду тестирования (UI Automator, Espresso, JUnit4, Appium, Detox, Calabash).

Если приложение является многоязычным, разработчики могут проверить правильность работы функций приложения в разных языковых стандартах.

Как это работает

Чтобы интегрировать библиотеку тестов App Actions в среду тестирования приложения, разработчикам следует создать новые или обновить существующие 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

Добавление зависимостей библиотеки тестов App Actions

  1. Добавьте репозиторий Google в список репозиториев проекта в settings.gradle :

        allprojects {
            repositories {
                
                google()
            }
        }
    
  2. В файле build.gradle модуля приложения добавьте зависимости AATL:

        androidTestImplementation 'com.google.assistant.appactions:testing:1.0.0'
    

    Обязательно используйте номер версии загруженной вами библиотеки.

Создание интеграционных тестов

  1. Создайте новые тесты в app/src/androidTest . Для тестов Robolectric создайте их в app/src/test :

    Котлин

      
        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…")
        }
      

    Ява

      
        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, вам необходимо изменить способ запуска действия на основе результатов AATL. Вот пример для Espresso с использованием метода ActivityScenario :

    Котлин

        
        ActivityScenario.launch<MainActivity>(intentResult.intent);
        Espresso.onView(ViewMatchers.withId(R.id.startActivityTitle))
          .check(ViewAssertions.matches(ViewMatchers.withText("Launching…")))
        

    Ява

        
          ActivityScenario.launch<MainActivity>(intentResult.intent);
          Espresso.onView(ViewMatchers.withId(R.id.startActivityTitle))
            .check(ViewAssertions.matches(ViewMatchers.withText("Launching…")))
        
  2. Убедитесь, что свойства имени и ключа в сопоставлениях параметров соответствуют параметрам из BII. Например, exercisePlan.forExercise.name соответствует документации для параметра в GET_EXERCISE_PLAN .

  3. Создайте экземпляр API с параметром контекста Android (полученным из ApplicationProvider или InstrumentationRegistry ):

    • Архитектура приложения с одним модулем:

    Котлин

        
          private lateinit var aatl: AppActionsTestManager
          @Before
          fun init() {
            val appContext = ApplicationProvider.getApplicationContext()
            aatl = AppActionsTestManager(appContext)
          }
        
      

    Ява

        
          private AppActionsTestManager aatl;
    
          @Before
          public void init() {
            Context appContext = ApplicationProvider.getApplicationContext();
            aatl = new AppActionsTestManager(appContext);
          }
        
      
    • Многомодульная архитектура приложения:

    Котлин

        
          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)
          }
        
      

    Ява

        
          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));
          }
        
      
  4. Выполните метод fulfill API и получите объект AppActionsFulfillmentResult .

Выполнение утверждений

Рекомендуемый способ утверждения библиотеки тестов действий приложения:

  1. Укажите тип выполнения AppActionsFulfillmentResult . Это должен быть FulfillmentType.INTENT или FulfillmentType.UNFULFILLED , чтобы проверить, как приложение ведет себя в случае неожиданных запросов BII.
  2. Существует два варианта выполнения: выполнение INTENT и DEEPLINK .
    • Обычно разработчик может различать выполнение INTENT и DEEPLINK , просматривая тег намерения в shortcuts.xml , который они выполняют, запуская библиотеку.
    • Если под тегом намерения есть тег шаблона URL-адреса, это означает, что DEEPLINK выполняет это намерение.
    • Если метод getData() результирующего намерения возвращает ненулевой объект, это также указывает на выполнение DEEPLINK . Аналогично, если getData возвращает null это означает, что это выполнение INTENT .
  3. В случае INTENT введите AppActionsFulfillmentResult в AppActionsIntentFulfillmentResult , получите намерение Android, вызвав метод getIntent , и выполните одно из следующих действий:
    • Утвердите отдельные поля Android Intent.
    • Укажите uri намерения, доступ к которому осуществляется с помощью метода Intent.getData.getHost.
  4. В случае DEEPLINK выполните приведение AppActionsFulfillmentResult к AppActionsIntentFulfillmentResult (так же, как для сценария INTENT выше), получите намерение Android, вызвав метод getIntent , и утвердите URL-адрес глубокой ссылки (доступ к которому осуществляется через intent.getData.getHost ).
  5. Как для INTENT , так и DEEPLINK вы можете использовать полученное намерение для запуска активности с выбранной платформой тестирования Android.

Интернационализация

Если ваше приложение имеет несколько языковых стандартов, вы можете настроить тесты для запуска определенного тестируемого языкового стандарта. Альтернативно вы можете напрямую изменить локаль:

Котлин

    
    import android.content.res.Configuration
    import java.util.Locale
    ...
    val newLocale = Locale("es")
    val conf = context.resources.configuration
    conf = Configuration(conf)
    conf.setLocale(newLocale)
    
  

Ява

    
    Locale newLocale = new Locale("es");
    Configuration conf = context.getResources().getConfiguration();
    conf = new Configuration(conf);
    conf.setLocale(newLocale);
    
  

Вот пример теста AATL, настроенного для испанского языка (ES):

Котлин

      
      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")
        }
      }
      
    

Ява

      
      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) и новее.
,

Библиотека тестирования действий приложения (AATL) предоставляет разработчикам возможность программно тестировать выполнение действий приложения, автоматизируя тестирование, которое обычно выполняется с использованием реальных голосовых запросов или инструмента тестирования действий приложения.

Библиотека помогает убедиться в правильности конфигурации shortcut.xml и успешности описанного вызова намерения Android. Библиотека тестирования действий приложения предоставляет механизм для проверки способности вашего приложения выполнять заданные намерения и параметры Google Ассистента путем преобразования их в глубокую ссылку Android или намерение Android, которые можно утверждать и использовать для создания экземпляра действия Android.

Тестирование проводится в виде модульных Robolectric или инструментальных тестов в среде Android. Это позволяет разработчикам всесторонне тестировать свое приложение, эмулируя фактическое поведение приложения. Для тестирования BII, пользовательских намерений или выполнения глубоких ссылок можно использовать любую инструментальную среду тестирования (UI Automator, Espresso, JUnit4, Appium, Detox, Calabash).

Если приложение является многоязычным, разработчики могут проверить правильность работы функций приложения в различных языковых стандартах.

Как это работает

Чтобы интегрировать библиотеку тестов App Actions в среду тестирования приложения, разработчикам следует создать новые или обновить существующие 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

Добавление зависимостей библиотеки тестов App Actions

  1. Добавьте репозиторий Google в список репозиториев проекта в settings.gradle :

        allprojects {
            repositories {
                
                google()
            }
        }
    
  2. В файле build.gradle модуля приложения добавьте зависимости AATL:

        androidTestImplementation 'com.google.assistant.appactions:testing:1.0.0'
    

    Обязательно используйте номер версии загруженной вами библиотеки.

Создание интеграционных тестов

  1. Создайте новые тесты в app/src/androidTest . Для тестов Robolectric создайте их в app/src/test :

    Котлин

      
        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…")
        }
      

    Ява

      
        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, вам необходимо изменить способ запуска действия на основе результатов AATL. Вот пример для Espresso с использованием метода ActivityScenario :

    Котлин

        
        ActivityScenario.launch<MainActivity>(intentResult.intent);
        Espresso.onView(ViewMatchers.withId(R.id.startActivityTitle))
          .check(ViewAssertions.matches(ViewMatchers.withText("Launching…")))
        

    Ява

        
          ActivityScenario.launch<MainActivity>(intentResult.intent);
          Espresso.onView(ViewMatchers.withId(R.id.startActivityTitle))
            .check(ViewAssertions.matches(ViewMatchers.withText("Launching…")))
        
  2. Свойства имени и ключа в сопоставлениях параметров должны соответствовать параметрам из BII. Например, exercisePlan.forExercise.name соответствует документации для параметра в GET_EXERCISE_PLAN .

  3. Создайте экземпляр API с параметром контекста Android (полученным из ApplicationProvider или InstrumentationRegistry ):

    • Архитектура приложения с одним модулем:

    Котлин

        
          private lateinit var aatl: AppActionsTestManager
          @Before
          fun init() {
            val appContext = ApplicationProvider.getApplicationContext()
            aatl = AppActionsTestManager(appContext)
          }
        
      

    Ява

        
          private AppActionsTestManager aatl;
    
          @Before
          public void init() {
            Context appContext = ApplicationProvider.getApplicationContext();
            aatl = new AppActionsTestManager(appContext);
          }
        
      
    • Многомодульная архитектура приложения:

    Котлин

        
          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)
          }
        
      

    Ява

        
          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));
          }
        
      
  4. Выполните метод fulfill API и получите объект AppActionsFulfillmentResult .

Выполнение утверждений

Рекомендуемый способ утверждения библиотеки тестов действий приложения:

  1. Укажите тип выполнения AppActionsFulfillmentResult . Это должен быть FulfillmentType.INTENT или FulfillmentType.UNFULFILLED , чтобы проверить, как приложение ведет себя в случае неожиданных запросов BII.
  2. Существует два варианта выполнения: выполнение INTENT и DEEPLINK .
    • Обычно разработчик может различать выполнение INTENT и DEEPLINK , просматривая тег намерения в shortcuts.xml , который они выполняют, запуская библиотеку.
    • Если под тегом намерения есть тег шаблона URL-адреса, это означает, что DEEPLINK выполняет это намерение.
    • Если метод getData() результирующего намерения возвращает ненулевой объект, это также указывает на выполнение DEEPLINK . Аналогично, если getData возвращает null это означает, что это выполнение INTENT .
  3. В случае INTENT введите AppActionsFulfillmentResult в AppActionsIntentFulfillmentResult , получите намерение Android, вызвав метод getIntent , и выполните одно из следующих действий:
    • Утвердите отдельные поля Android Intent.
    • Укажите uri намерения, доступ к которому осуществляется с помощью метода Intent.getData.getHost.
  4. В случае DEEPLINK введите AppActionsFulfillmentResult в AppActionsIntentFulfillmentResult (так же, как и для сценария INTENT выше), получите намерение Android, вызвав метод getIntent , и утвердите URL-адрес глубокой ссылки (доступ к которому осуществляется через intent.getData.getHost ).
  5. Как для INTENT , так и DEEPLINK вы можете использовать полученное намерение для запуска активности с выбранной платформой тестирования Android.

Интернационализация

Если ваше приложение имеет несколько языковых стандартов, вы можете настроить тесты для запуска определенного тестируемого языкового стандарта. Альтернативно вы можете напрямую изменить локаль:

Котлин

    
    import android.content.res.Configuration
    import java.util.Locale
    ...
    val newLocale = Locale("es")
    val conf = context.resources.configuration
    conf = Configuration(conf)
    conf.setLocale(newLocale)
    
  

Ява

    
    Locale newLocale = new Locale("es");
    Configuration conf = context.getResources().getConfiguration();
    conf = new Configuration(conf);
    conf.setLocale(newLocale);
    
  

Вот пример теста AATL, настроенного для испанского языка (ES):

Котлин

      
      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")
        }
      }
      
    

Ява

      
      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) и новее.