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

ไลบรารีการทดสอบการดำเนินการของแอป (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)

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

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

หากต้องการผสานรวมไลบรารีการทดสอบการดำเนินการของแอปเข้ากับสภาพแวดล้อมการทดสอบของแอป นักพัฒนาแอปควร สร้างการทดสอบใหม่หรืออัปเดตการทดสอบแบบ 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 ของไลบรารีการทดสอบการดำเนินการของแอป

  1. เพิ่มที่เก็บของ Google ลงในรายการที่เก็บของโปรเจ็กต์ใน settings.gradle

        allprojects {
            repositories {
                …
                google()
            }
        }
    
  2. ในไฟล์โมดูล build.gradle ของแอป ให้เพิ่มทรัพยากร Dependency ของ 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 ดึงข้อมูล Android Intent ด้วยการเรียกใช้ 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 เพื่อรับข้อความเตือนหรือข้อความระดับข้อผิดพลาด คุณยังสามารถเพิ่มการบันทึก ระดับเพื่อบันทึกเอาต์พุตเพิ่มเติมจาก ไลบรารี

ข้อจำกัด

ข้อจำกัดในปัจจุบันของไลบรารีการทดสอบการดำเนินการของแอปมีดังนี้

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