App Actions-Testbibliothek

Mit der App Actions Test Library (AATL) können Entwickler die App-Aktion-Ausführung programmatisch testen und so Tests automatisieren, die normalerweise mit echten Sprachanfragen oder dem App Actions-Testtool durchgeführt werden.

Die Bibliothek sorgt dafür, dass die shortcut.xml-Konfiguration korrekt und der beschriebene Android-Intent-Aufruf erfolgreich ist. App Actions-Testbibliothek einen Mechanismus bietet, mit dem Sie testen können, ob Ihre App Assistant-Intent und -Parameter, indem sie in einen Android-Deeplink umgewandelt oder Android-Intent, der geltend gemacht und zur Instanziierung eines Android-Geräts verwendet werden kann. Aktivitäten.

Die Tests werden in Form von Robolectric-Einheits- oder instrumentierten Tests in der Android-Umgebung durchgeführt. So können Entwickelnde ihre indem Sie das tatsächliche App-Verhalten emulieren. Zum Testen von BIIs, benutzerdefiniert Intents oder Deeplink-Fulfillment nutzen, kann jedes instrumentierte Test-Framework verwendet (UI Automator, Espresso, JUnit4, Appium, Detox, Calabash).

Wenn die Anwendung mehrsprachig ist, können Entwickler prüfen, ob die Funktionen der Anwendung in verschiedenen Sprachen korrekt funktionieren.

Funktionsweise

Wenn Entwickler die App Actions Test Library in die Testumgebung der App einbinden möchten, sollten sie neue Robolectric- oder instrumentierte Tests im app-Modul der App erstellen oder vorhandene aktualisieren.

Der Testcode enthält die folgenden Teile:

  • Initialisierung der Bibliotheksinstanz in der gemeinsamen Einrichtungsmethode oder in einzelne Testfälle.
  • Bei jedem einzelnen Test wird die fulfill-Methode der Bibliotheksinstanz aufgerufen, um das Ergebnis der Intent-Erstellung zu erhalten.
  • Der Entwickler bestätigt dann den Deeplink oder löst die App-Ausführung aus und führt eine benutzerdefinierte Validierung des App-Status durch.

Voraussetzungen für die Einrichtung

Damit Sie die Testbibliothek verwenden können, ist eine anfängliche App-Konfiguration erforderlich, bevor Sie die Tests Ihrer App hinzufügen.

Konfiguration

Damit Sie die App Actions-Testbibliothek verwenden können, muss Ihre App so konfiguriert sein:

  • Installieren Sie das Android-Gradle-Plug-in (AGP).
  • Füge im Modul app im Ordner res/xml eine Datei „shortcuts.xml“ hinzu.
  • Achten Sie darauf, dass AndroidManifest.xml <meta-data android:name="android.app.shortcuts" android:resource=”@xml/shortcuts” /> enthält:
    • das <application>-Tag
    • das Launcher-<activity>-Tag
  • Platzieren Sie das <capability>-Element innerhalb des <shortcuts>-Elements in shortcuts.xml

Abhängigkeiten der App Actions-Testbibliothek hinzufügen

  1. Google-Repository zur Liste der Projekt-Repositories in hinzufügen settings.gradle:

        allprojects {
            repositories {
                
                google()
            }
        }
    
  2. Fügen Sie in der Datei build.gradle des App-Moduls die AATL-Abhängigkeiten hinzu:

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

    Verwenden Sie die Versionsnummer der heruntergeladenen Bibliothek.

Integrationstests erstellen

  1. Erstellen Sie neue Tests unter app/src/androidTest. Erstellen Sie für Robolectric-Tests sie unter 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…")
          }
      

    Wenn Sie Espresso verwenden, müssen Sie die Ausführung der Aktivität anhand der AATL-Ergebnisse anpassen. Hier ist ein Beispiel für Espresso mit der Methode ActivityScenario-Methode:

    Kotlin

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

    Java

        
          ActivityScenario.launch<MainActivity>(intentResult.intent);
          Espresso.onView(ViewMatchers.withId(R.id.startActivityTitle))
            .check(ViewAssertions.matches(ViewMatchers.withText("Launching…")))
        
  2. Die Namen und Schlüsseleigenschaften in den Parameterzuordnungen müssen mit den Parametern aus dem BII übereinstimmen. Beispiel: exercisePlan.forExercise.name stimmt mit der Dokumentation für den Parameter in GET_EXERCISE_PLAN überein.

  3. API-Instanz mit dem Android-Kontextparameter erstellen (aus ApplicationProvider oder InstrumentationRegistry abgerufen):

    • Anwendungsarchitektur mit einem Modul:

    Kotlin

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

    Java

        
          private AppActionsTestManager aatl;
    
          @Before
          public void init() {
            Context appContext = ApplicationProvider.getApplicationContext();
            aatl = new AppActionsTestManager(appContext);
          }
        
      
    • Architektur von Anwendungen mit mehreren Modulen:

    Kotlin

        
          private lateinit var aatl: AppActionsTestManager
    
          @Before
          fun init() {
            val appContext = ApplicationProvider.getApplicationContext()
            val lookupPackages = listOf("com.myapp.mainapp", "com.myapp.resources")
            aatl = AppActionsTestManager(appContext, lookupPackages)
          }
        
      

    Java

        
          private AppActionsTestManager aatl;
    
          @Before
          public void init() throws Exception {
    
            Context appContext = ApplicationProvider.getApplicationContext();
            List<String> lookupPackages = Arrays.asList("com.myapp.mainapp","com.myapp.resources");
            aatl = new AppActionsTestManager(appContext, Optional.of(lookupPackages));
          }
        
      
  4. Führen Sie die Methode fulfill der API aus und rufen Sie das AppActionsFulfillmentResult-Objekt ab.

Behauptungen prüfen

Die empfohlene Methode zum Bestätigen der App Actions-Testbibliothek ist:

  1. Geben Sie den Auftragsausführungstyp der AppActionsFulfillmentResult an. Es muss FulfillmentType.INTENT oder FulfillmentType.UNFULFILLED sein, um Sie können testen, wie sich die App bei unerwarteten BII-Anfragen verhält.
  2. Es gibt zwei Arten der Auftragsausführung: INTENT und DEEPLINK.
    • Normalerweise kann der Entwickler zwischen INTENT und DEEPLINK Ausführungen anhand des Intent-Tags in shortcuts.xml die sie erfüllen, indem sie die Bibliothek auslösen.
    • Wenn sich unter dem Intent-Tag ein „url-template“-Tag befindet, erfüllt die DEEPLINK diesen Intent.
    • Wenn die getData()-Methode der Ergebnisabsicht ein nicht nullwertiges Objekt zurückgibt, weist dies auch auf eineDEEPLINK-Erfüllung hin. Wenn getData den Wert null zurückgibt, bedeutet das, dass es sich um eine INTENT-Ausführung handelt.
  3. Für den Fall INTENT: Führe einen Typumwandlung von AppActionsFulfillmentResult in AppActionsIntentFulfillmentResult durch, hole den Android-Intent durch Aufrufen der Methode getIntent ab und führe einen der folgenden Schritte aus:
    • Einzelne Felder des Android-Intents angeben
    • URI eines Intents bestätigen, auf den über intent.getData.getHost-Methode.
  4. Für den Fall DEEPLINK AppActionsFulfillmentResult in AppActionsIntentFulfillmentResult umwandeln (wie oben für das INTENT-Szenario), Android-Intent durch Aufrufen der getIntent-Methode abrufen und Deeplink-URL prüfen (über intent.getData.getHost darauf zugreifen).
  5. Sowohl für INTENT als auch für DEEPLINK können Sie die resultierende Intent-Anfrage verwenden, um die Aktivität mit dem ausgewählten Android-Testframework zu starten.

Lokalisierung

Wenn Ihre App mehrere Sprachen unterstützt, können Sie Tests so konfigurieren, dass ein bestimmter Test für eine bestimmte Sprache ausgeführt wird. Alternativ können Sie das Gebietsschema direkt ändern:

Kotlin

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

Java

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

Hier ein Beispiel für einen AATL-Test, der für die Sprache „Spanisch (ES)“ konfiguriert ist:

Kotlin

      
      import com.google.common.truth.Truth.assertThat
      import org.junit.Assert.assertEquals
      import android.content.Context
      import android.content.res.Configuration
      import androidx.test.platform.app.InstrumentationRegistry
      import com.google.assistant.appactions.testing.aatl.AppActionsTestManager
      import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentIntentResult
      import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentResult
      import com.google.assistant.appactions.testing.aatl.fulfillment.FulfillmentType
      import com.google.common.collect.ImmutableMap
      import java.util.Locale
      import org.junit.Before
      import org.junit.Test
      import org.junit.runner.RunWith
      import org.robolectric.RobolectricTestRunner

      @RunWith(RobolectricTestRunner::class)
      class ShortcutForDifferentLocaleTest {

        @Before
        fun setUp() {
          val context = InstrumentationRegistry.getInstrumentation().getContext()

          // change the device locale to 'es'
          val newLocale = Locale("es")
          val conf = context.resources.configuration
          conf = Configuration(conf)
          conf.setLocale(newLocale)

          val localizedContext = context.createConfigurationContext(conf)
        }

        @Test
        fun shortcutForDifferentLocale_succeeds() {
          val aatl = AppActionsTestManager(localizedContext)
          val intentName = "actions.intent.GET_EXERCISE_PLAN"
          val intentParams = ImmutableMap.of("exercisePlan.forExercise.name", "Running")

          val result = aatl.fulfill(intentName, intentParams)
          assertThat(result.getFulfillmentType()).isEqualTo(FulfillmentType.INTENT)

          val intentResult = result as AppActionsFulfillmentIntentResult

          assertThat(intentResult.getIntent().getData().toString())
            .isEqualTo("myexercise://browse?plan=running_weekly")
        }
      }
      
    

Java

      
      import static com.google.common.truth.Truth.assertThat;
      import static org.junit.Assert.assertEquals;

      import android.content.Context;
      import android.content.res.Configuration;
      import androidx.test.platform.app.InstrumentationRegistry;
      import com.google.assistant.appactions.testing.aatl.AppActionsTestManager;
      import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentIntentResult;
      import com.google.assistant.appactions.testing.aatl.fulfillment.AppActionsFulfillmentResult;
      import com.google.assistant.appactions.testing.aatl.fulfillment.FulfillmentType;
      import com.google.common.collect.ImmutableMap;
      import java.util.Locale;
      import org.junit.Before;
      import org.junit.Test;
      import org.junit.runner.RunWith;
      import org.robolectric.RobolectricTestRunner;

      @Test
      public void shortcutForDifferentLocale_succeeds() throws Exception {
        Context context = InstrumentationRegistry.getInstrumentation().getContext();

        // change the device locale to 'es'
        Locale newLocale = new Locale("es");
        Configuration conf = context.getResources().getConfiguration();
        conf = new Configuration(conf);
        conf.setLocale(newLocale);

        Context localizedContext = context.createConfigurationContext(conf);

        AppActionsTestManager aatl = new AppActionsTestManager(localizedContext);
        String intentName = "actions.intent.GET_EXERCISE_PLAN";
        ImmutableMap<String, String> intentParams = ImmutableMap.of("exercisePlan.forExercise.name", "Running");

        AppActionsFulfillmentResult result = aatl.fulfill(intentName, intentParams);
        assertThat(result.getFulfillmentType()).isEqualTo(FulfillmentType.INTENT);

        AppActionsFulfillmentIntentResult intentResult = (AppActionsFulfillmentIntentResult) result;

        assertThat(intentResult.getIntent().getData().toString())
          .isEqualTo("myexercise://browse?plan=running_weekly");
      }
      
    

Fehlerbehebung

Wenn Ihr Integrationstest unerwartet fehlschlägt, können Sie nach AATL-Logmeldungen suchen im Android Studio-Logcat-Fenster ein, um die Warnung oder Fehlermeldung zu erhalten. Sie können auch den Logging-Level erhöhen, um mehr Ausgabe aus der Bibliothek zu erfassen.

Beschränkungen

Dies sind die aktuellen Einschränkungen der App Actions Test Library:

  • AATL testet keine Natural Language Understanding (NLU)- oder Spracherkennungsfunktionen (Speech-to-Text, STT).
  • AATL funktioniert nicht, wenn sich Tests in Modulen außerhalb der Standard-App befinden -Modul.
  • AATL ist nur mit Android 7.0 „Nougat“ kompatibel (API-Level 24) und neuer.