ไลบรารีการทดสอบการดำเนินการของแอป

ไลบรารีการทดสอบการดำเนินการของแอป (AATL) มอบความสามารถที่จะช่วยให้นักพัฒนาซอฟต์แวร์ เพื่อทดสอบการดำเนินการของแอป Fulfillment แบบเป็นโปรแกรม ทำให้การทดสอบเป็นไปโดยอัตโนมัติ โดยปกติจะทำโดยใช้คำสั่งเสียงจริงหรือเครื่องมือทดสอบการดำเนินการของแอป

ไลบรารีช่วยให้มั่นใจได้ว่าการกำหนดค่า shortcut.xml ถูกต้องและ การเรียกใช้ Intent ของ Android ที่อธิบายไว้สำเร็จ ไลบรารีการทดสอบการดำเนินการของแอป เป็นกลไกในการทดสอบความสามารถของแอปในการดำเนินการตามที่กำหนดโดย Google Intent และพารามิเตอร์ของ Assistant โดยแปลงเป็น Deep Link สำหรับ Android หรือ Intent ของ Android ซึ่งสามารถยืนยันและใช้ในการสร้างอินสแตนซ์ Android กิจกรรม

การทดสอบจะดำเนินการในรูปแบบของหน่วย Robolectric หรือการทดสอบแบบมีเครื่องวัดใน สภาพแวดล้อมของ Android ซึ่งช่วยให้นักพัฒนาแอปสามารถทดสอบ โดยจำลองลักษณะการทำงานจริงของแอป สําหรับการทดสอบ BII, Intent ที่กําหนดเอง หรือการตอบสนองของ Deep Link คุณสามารถใช้เฟรมเวิร์กการทดสอบที่มีเครื่องมือวัดผลได้ (UI Automator, Espresso, JUnit4, Appium, Detox, Calabash)

หากแอปพลิเคชันเป็นหลายภาษา นักพัฒนาแอปสามารถตรวจสอบว่าฟังก์ชันการทํางานของแอปพลิเคชันทำงานได้อย่างถูกต้องในภาษาต่างๆ

วิธีการทำงาน

หากต้องการผสานรวม App Actions Test Library ภายในสภาพแวดล้อมการทดสอบของแอป นักพัฒนาแอปควรสร้างใหม่หรืออัปเดตการทดสอบ Robolectric หรือการทดสอบที่มีเครื่องมือวัดผลที่มีอยู่ในapp โมดูลของแอป

รหัสทดสอบประกอบด้วยส่วนต่อไปนี้

  • เริ่มต้นอินสแตนซ์ไลบรารีในวิธีการตั้งค่าทั่วไปหรือในเทสเคสแต่ละรายการ
  • การทดสอบแต่ละรายการเรียกใช้เมธอด fulfill ของอินสแตนซ์ไลบรารีเพื่อสร้างผลลัพธ์การสร้าง Intent
  • จากนั้นนักพัฒนาซอฟต์แวร์จะยืนยัน Deep Link หรือทริกเกอร์ Fulfillment ของแอป และเรียกใช้การตรวจสอบที่กำหนดเองในสถานะของแอป

ข้อกำหนดในการตั้งค่า

หากต้องการใช้คลังทดสอบ คุณจะต้องทำการกําหนดค่าแอปเบื้องต้นก่อนเพิ่มการทดสอบลงในแอปพลิเคชัน

การกำหนดค่า

หากต้องการใช้คลังทดสอบการดำเนินการของแอป ให้ตรวจสอบว่าได้กําหนดค่าแอปดังนี้

  • ติดตั้งปลั๊กอิน Android Gradle (AGP)
  • รวมไฟล์ shortcuts.xml ในโฟลเดอร์ res/xml ในโมดูล app
  • ตรวจสอบว่า AndroidManifest.xml มี <meta-data android:name="android.app.shortcuts" android:resource=”@xml/shortcuts” /> ใต้
    • แท็ก <application>
    • แท็ก Launcher <activity>
  • วางองค์ประกอบ <capability> ภายในองค์ประกอบ <shortcuts> ใน shortcuts.xml

เพิ่มทรัพยากร Dependency ของ App Actions Test Library

  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:

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

    หากใช้ Espresso คุณต้องแก้ไขวิธีเปิดใช้งานกิจกรรมตามผลลัพธ์ AATL ต่อไปนี้คือตัวอย่างสำหรับ Espresso ที่ใช้ เมธอด 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. ตั้งชื่อและพร็อพเพอร์ตี้คีย์ในการแมปพารามิเตอร์ตรงกับ จาก BII ตัวอย่างเช่น exercisePlan.forExercise.name ตรงกับ เอกสารประกอบของพารามิเตอร์ใน GET_EXERCISE_PLAN

  3. สร้างอินสแตนซ์ API ที่มีพารามิเตอร์บริบทของ Android (ได้รับจาก ApplicationProvider หรือ InstrumentationRegistry):

    • สถาปัตยกรรมแอปโมดูลเดียว:

    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);
          }
        
      
    • สถาปัตยกรรมแอปหลายโมดูล:

    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. เรียกใช้เมธอด fulfill ของ API และรับออบเจ็กต์ AppActionsFulfillmentResult

ดำเนินการยืนยัน

วิธีที่แนะนำในการยืนยันไลบรารีการดำเนินการของแอปคือ

  1. ยืนยันประเภทการดำเนินการตามคำสั่งซื้อของ AppActionsFulfillmentResult โดยต้องเป็น FulfillmentType.INTENT หรือ FulfillmentType.UNFULFILLED เพื่อทดสอบลักษณะการทํางานของแอปในกรณีที่มีการส่งคําขอ BII ที่ไม่คาดคิด
  2. การดำเนินการตามคำสั่งซื้อมี 2 รูปแบบ ได้แก่ การดำเนินการตามคำสั่งซื้อ INTENT และ DEEPLINK
    • โดยปกติแล้ว นักพัฒนาซอฟต์แวร์สามารถแยกความแตกต่างระหว่าง INTENT กับ ดำเนินการตามคำสั่งซื้อ DEEPLINK รายการโดยดูที่แท็ก Intent ใน shortcuts.xml ที่ผู้ใช้ตอบสนองด้วยการเรียกไลบรารี
    • หากมีแท็กเทมเพลต URL อยู่ใต้แท็ก Intent ระบบจะแสดง ว่า DEEPLINK ตอบสนองต่อความตั้งใจนี้
    • หากเมธอด getData() ของ Intent ของผลการค้นหาแสดงออบเจ็กต์ที่ไม่ใช่ Null และยังหมายถึงDEEPLINKการดำเนินการตามคำสั่งซื้อด้วย ในทำนองเดียวกัน หาก getData แสดงผล null หมายความว่าเป็นการดำเนินการตามคำสั่งซื้อ INTENT
  3. สำหรับเคส INTENT ให้เปลี่ยนประเภท AppActionsFulfillmentResult เป็น AppActionsIntentFulfillmentResult, ดึงข้อมูล Intent ของ Android โดยการเรียกใช้เมธอด getIntent แล้วทําอย่างใดอย่างหนึ่งต่อไปนี้
    • ยืนยันช่องต่างๆ ของ Android Intent
    • ยืนยัน URI ของ Intent ที่มีการเข้าถึงผ่าน เมธอดIntent.getData.getHost
  4. สำหรับเคส DEEPLINK ให้พิมพ์ AppActionsFulfillmentResult ถึง AppActionsIntentFulfillmentResult (เช่นเดียวกับสถานการณ์ INTENT ด้านบน) ดึงข้อมูล Android Intent ด้วยการเรียกใช้เมธอด getIntent และยืนยัน URL ของ Deep Link (เข้าถึงผ่าน intent.getData.getHost)
  5. สําหรับทั้ง INTENT และ DEEPLINK คุณสามารถใช้ Intent ที่ได้เพื่อเปิดใช้งานกิจกรรมด้วยเฟรมเวิร์กการทดสอบ Android ที่เลือก

การทำให้เป็นสากล

หากแอปมีหลายภาษา คุณจะกำหนดค่าการทดสอบให้เรียกใช้ภาษาที่ต้องการทดสอบได้ หรือจะเปลี่ยนภาษาโดยตรงก็ได้ ดังนี้

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

ต่อไปนี้คือตัวอย่างการทดสอบ AATL ที่กําหนดค่าสําหรับภาษาสเปน (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");
      }
      
    

แก้ปัญหา

หากการทดสอบการผสานรวมล้มเหลวโดยไม่คาดคิด คุณสามารถมองหาข้อความบันทึก AATL ในหน้าต่าง Logcat ของ Android Studio เพื่อรับข้อความเตือนหรือข้อความระดับข้อผิดพลาด นอกจากนี้ คุณยังเพิ่มระดับการบันทึกเพื่อบันทึกเอาต์พุตจากคลังได้มากขึ้น

ข้อจำกัด

ข้อจํากัดปัจจุบันของคลังการทดสอบ App Actions มีดังนี้

  • AATL ไม่ได้ทดสอบฟีเจอร์การทําความเข้าใจภาษาธรรมชาติ (NLU) หรือฟีเจอร์การแปลงคำพูดเป็นข้อความ (STT)
  • AATL ไม่ทำงานเมื่อการทดสอบอยู่ในโมดูลอื่นที่ไม่ใช่แอปเริ่มต้น
  • AATL ใช้ได้กับ Android 7.0 "Nougat" (API ระดับ 24) ขึ้นไปเท่านั้น