Libreria di test delle azioni app

La libreria di test delle Azioni app (AATL) offre funzionalità per consentire agli sviluppatori per testare il completamento delle Azioni app in modo programmatico, automatizzando i test che di solito vengono eseguite utilizzando query vocali reali o lo strumento di test delle Azioni app.

La libreria contribuisce a garantire che la configurazione di shortcut.xml sia corretta e la chiamata dell'intent Android descritta ha esito positivo. Libreria di test delle Azioni app offre un meccanismo per testare la capacità dell'app di soddisfare i requisiti di Google, Intent e parametri dell'assistente, convertendoli in un link diretto Android oppure Per intent Android, che può essere rivendicato e utilizzato per creare un'istanza di un Android attività.

I test vengono eseguiti sotto forma di unità robotica o test strumentati in dell'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 la funzionalità dell'applicazione funzioni correttamente in diversi paesi.

Come funziona

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

Il codice di test contiene le seguenti parti:

  • Inizializzazione dell'istanza della libreria, nel metodo di configurazione comune o in singoli scenari di test.
  • Ogni singolo test chiama il metodo fulfill dell'istanza della libreria a per produrre il risultato della creazione dell'intent.
  • Lo sviluppatore quindi dichiara il link diretto o attiva il completamento dell'app. ed esegue una convalida personalizzata per lo stato dell'app.

Requisiti per la configurazione

Per poter utilizzare la libreria di test, sono necessarie alcune configurazioni iniziali dell'app richiesta prima di aggiungere i test all'applicazione.

Configurazione

Per utilizzare la Raccolta di test delle Azioni app, assicurati che l'app sia configurata come segue:

  • Installa il plug-in Android Gradle (AGP)
  • Includi un file shortcuts.xml nella cartella res/xml nel 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 le dipendenze della libreria di test di App Actions

  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 robotici, crea sotto 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 usi Espresso, devi modificare la modalità di avvio dell'Attività. in base ai risultati di AATL. Ecco un esempio per Espresso in cui viene utilizzata la 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. Fai in modo che il nome e le proprietà della chiave nelle mappature dei parametri corrispondano al dall'intent integrato. Ad esempio, exercisePlan.forExercise.name corrisponde alla documentazione per il parametro in GET_EXERCISE_PLAN.

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

    • Architettura dell'app a modulo singolo:

    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 delle app multimodulo:

    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 FulfillmentType.INTENT o FulfillmentType.UNFULFILLED per testare il comportamento dell'app in caso di richieste di intent integrati impreviste.
  2. Esistono due tipi di esecuzione: INTENT e DEEPLINK.
    • Di solito, lo sviluppatore è in grado di distinguere tra INTENT e DEEPLINK completamenti esaminando il tag di intent in shortcuts.xml che completano attivando la raccolta.
    • 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, questo indica anche il completamento di DEEPLINK. Allo stesso modo, se getData restituisce null, significa che si tratta di un fulfillment INTENT.
  3. Per il caso INTENT, digita AppActionsFulfillmentResult in AppActionsIntentFulfillmentResult, recupera l'intent Android chiamando 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);
    
  

Di seguito è riportato 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.