Biblioteka testowa akcji w aplikacji

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 folderu res/xml w module app.
  • 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.
  • Umieść element <capability> wewnątrz elementu <shortcuts> w shortcuts.xml

Dodawanie zależności biblioteki testów działań aplikacji

  1. Dodaj repozytorium Google do listy repozytoriów projektu w settings.gradle:

        allprojects {
            repositories {
                
                google()
            }
        }
    
  2. 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

  1. Utwórz nowe testy w sekcji app/src/androidTest. W przypadku testów Robolectric utwórz je w ramach 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…")
          }
      

    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…")))
        
  2. 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 w GET_EXERCISE_PLAN.

  3. Tworzenie instancji interfejsu API z parametrem Android Context (uzyskanym z ApplicationProvider lub InstrumentationRegistry):

    • 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));
          }
        
      
  4. Wykonaj metodę fulfill interfejsu API i uzyskaj AppActionsFulfillmentResult obiekt.

Wykonywanie stwierdzeń

Zalecamy następujące sposoby weryfikowania Biblioteki testów działań aplikacji:

  1. Określ typ realizacji zamówienia AppActionsFulfillmentResult. Musi to być FulfillmentType.INTENT lub FulfillmentType.UNFULFILLED, aby przetestować, jak aplikacja zachowuje się w przypadku nieoczekiwanych żądań BII.
  2. Istnieją 2 rodzaje realizacji: INTENT i DEEPLINK.
    • Normalnie deweloper może odróżnić INTENT od DEEPLINK 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 warunku DEEPLINK. Podobnie, jeśli getData zwraca null, co oznacza, że jest to realizacja INTENT.
  3. W przypadku INTENT przypadku rzutu typu AppActionsFulfillmentResult na AppActionsIntentFulfillmentResult, pobierz intencję Androida za pomocą wywołania getIntent 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.
  4. W przypadku DEEPLINK przypadku rzutu typu AppActionsFulfillmentResult na AppActionsIntentFulfillmentResult (tak samo jak w scenariuszu INTENT powyżej), pobierz intencję Androida, wywołując metodę getIntent i zatwierdzając URL precyzyjnego linku (dostępny za pomocą elementu intent.getData.getHost).
  5. W przypadku zasobów typu INTENT i DEEPLINK 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.