Libreria di test delle azioni app

La libreria di test App Actions (AATL) fornisce funzionalità che consentono agli sviluppatori di testare in modo programmatico l'aggiornamento delle azioni dell'app, automatizzando i test che normalmente vengono eseguiti utilizzando query vocali effettive o lo strumento di test App Actions.

La raccolta contribuisce ad assicurare che la configurazione di shortcut.xml sia corretta e che l'invocazione dell'intent Android descritta vada a buon fine. La libreria di test di App Actions fornisce un meccanismo per verificare la capacità della tua app di soddisfare determinati intent e parametri dell'Assistente Google, convertendoli in un link diretto Android o in un intent Android, che può essere verificato e utilizzato per creare un'attività Android.

I test vengono eseguiti sotto forma di test delle unità o test con strumenti Robolectric nell'ambiente Android. In questo modo, gli sviluppatori possono testare in modo completo la loro applicazione emulando il comportamento effettivo dell'app. Per testare gli intent integrati, intent o completamento di link diretti, qualsiasi framework di test strumentato può utilizzato (UI Automator, Espresso, JUnit4, Appium, Detox, Calabash).

Se l'applicazione è multilingue, gli sviluppatori possono verificare che il comportamento della funzionalità dell'applicazione è corretto in diverse impostazioni internazionali.

Come funziona

Per integrare la libreria di test delle Azioni app nell'ambiente di test dell'app, gli sviluppatori devono crea nuovi test robot o strumentati o aggiorna quelli esistenti su app dell'app.

Il codice di test è costituito dalle seguenti parti:

  • Inizializzazione dell'istanza della libreria, nel metodo di configurazione comune o nei singoli casi di test.
  • Ogni singolo test chiama il metodo fulfill dell'istanza della libreria per produrre il risultato della creazione dell'intent.
  • Lo sviluppatore verifica il link diretto o attiva l'app fulfillment esegue la convalida personalizzata dello stato dell'app.

Requisiti per la configurazione

Per utilizzare la libreria di test, è necessaria una configurazione iniziale dell'app prima di aggiungere i test all'applicazione.

Configurazione

Per utilizzare la libreria di test di App Actions, assicurati che la tua app sia configurata come segue:

  • Installa il plug-in Android per Gradle (AGP)
  • Includi un file shortcuts.xml nella cartella res/xml del modulo app.
  • Assicurati che AndroidManifest.xml includa <meta-data android:name="android.app.shortcuts" android:resource=”@xml/shortcuts” /> in:
    • il tag <application>
    • Il tag Avvio app <activity>
  • Posiziona l'elemento <capability> all'interno dell'elemento <shortcuts> in shortcuts.xml

Aggiungi dipendenze della libreria di test delle Azioni app

  1. Aggiungi il repository Google all'elenco dei repository del progetto in settings.gradle:

        allprojects {
            repositories {
                
                google()
            }
        }
    
  2. Nel file build.gradle del modulo dell'app, aggiungi le dipendenze AATL:

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

    Assicurati di utilizzare il numero di versione della libreria che hai scaricato.

Crea test di integrazione

  1. Crea nuovi test in app/src/androidTest. Per i test Robolectric, creali in 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…")
          }
      

    Se utilizzi Espresso, devi modificare il modo in cui avvii l'attività in base ai risultati AATL. Ecco un esempio per Espresso che utilizza il metodo 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. Le proprietà name e key nelle mappature dei parametri corrispondano ai parametri del BII. Ad esempio, exercisePlan.forExercise.name corrisponde alla documentazione per il parametro in GET_EXERCISE_PLAN.

  3. Istanza l'istanza dell'API con il parametro Android Context (ottenuto da ApplicationProvider o InstrumentationRegistry):

    • Architettura dell'app a un modulo:

    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);
          }
        
      
    • Architettura dell'app multi-modulo:

    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. Esegui il metodo fulfill dell'API e ottieni Oggetto AppActionsFulfillmentResult.

Eseguire asserzioni

Il modo consigliato per dichiarare la libreria di test delle Azioni app è:

  1. Verifica il tipo di evasione dell'AppActionsFulfillmentResult. Deve essere FulfillmentType.INTENT o FulfillmentType.UNFULFILLED per testare il comportamento dell'app in caso di richieste di intent integrati inaspettate.
  2. Esistono due tipi di esecuzione: INTENT e DEEPLINK.
    • In genere, lo sviluppatore può distinguere tra i completamenti INTENT e DEEPLINK osservando il tag intent in shortcuts.xml che sta completando attivando la libreria.
    • Se è presente un tag url-template sotto il tag per intent, questo indica che DEEPLINK soddisfi questo intento.
    • Se il metodo getData() dell'intent del risultato restituisce un oggetto non nullo, anche questo indicaDEEPLINK il completamento. Analogamente, se getData restituisce null significa che si tratta di un adempimento INTENT.
  3. Per il caso INTENT, digita AppActionsFulfillmentResult in AppActionsIntentFulfillmentResult, recupera l'intent Android richiamando getIntent ed esegui una delle seguenti operazioni:
    • Dichiarare i singoli campi di Android Intent.
    • Verifica l'URI di un'intent a cui si accede tramite il metodo intent.getData.getHost.
  4. Per il caso DEEPLINK, digita AppActionsFulfillmentResult in AppActionsIntentFulfillmentResult (come per lo scenario INTENT sopra), recupera l'intent Android richiamando il metodo getIntent e dichiara L'URL del link diretto (accessibile tramite intent.getData.getHost).
  5. Sia per INTENT che per DEEPLINK, puoi utilizzare l'intent risultante per avviare l'attività con il framework di test Android scelto.

Internazionalizzazione

Se la tua app ha più lingue, puoi configurare i test in modo da eseguire un determinato linguaggio sottoposto a test. In alternativa, puoi modificare direttamente le impostazioni internazionali:

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

Ecco un esempio di test AATL configurato per le impostazioni internazionali spagnole (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");
      }
      
    

Risoluzione dei problemi

Se il test di integrazione non riesce in modo imprevisto, puoi cercare i messaggi di log AATL nella finestra di logcat di Android Studio per ricevere il messaggio di avviso o di errore. Puoi anche aumentare il logging per acquisire più output nella libreria.

Limitazioni

Queste sono le limitazioni attuali della libreria di test delle Azioni app :

  • AATL non testa la comprensione del linguaggio naturale (NLU) o la conversione della voce in testo (STT).
  • AATL non funziona quando i test sono in moduli diversi dall'app predefinita in maggior dettaglio più avanti in questo modulo.
  • AATL è compatibile solo con Android 7.0 "Nougat" (livello API 24) e più recenti.