ไลบรารีการทดสอบการดำเนินการของแอป (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 ของไลบรารีการทดสอบการดำเนินการของแอป
เพิ่มที่เก็บของ Google ลงในรายการที่เก็บของโปรเจ็กต์ใน
settings.gradle
allprojects { repositories { … google() } }
ในไฟล์โมดูล
build.gradle
ของแอป ให้เพิ่มทรัพยากร Dependency ของ AATL ดังนี้androidTestImplementation 'com.google.assistant.appactions:testing:1.0.0'
ตรวจสอบว่าใช้หมายเลขเวอร์ชันของไลบรารีที่คุณดาวน์โหลดมา
สร้างการทดสอบการผสานรวม
สร้างการทดสอบใหม่ภายใต้
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…")))
ตั้งชื่อและพร็อพเพอร์ตี้คีย์ในการแมปพารามิเตอร์ตรงกับ จาก BII เช่น
exercisePlan.forExercise.name
ตรงกับเอกสารประกอบสําหรับพารามิเตอร์ในGET_EXERCISE_PLAN
สร้างอินสแตนซ์ 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)); }
เรียกใช้เมธอด
fulfill
ของ API และรับAppActionsFulfillmentResult
ออบเจ็กต์
ดำเนินการยืนยัน
วิธีที่แนะนำในการยืนยันไลบรารีการดำเนินการของแอปคือ
- ยืนยันประเภทการดำเนินการตามคำสั่งซื้อของ
AppActionsFulfillmentResult
ต้อง เป็นFulfillmentType.INTENT
หรือFulfillmentType.UNFULFILLED
เพื่อ ทดสอบลักษณะการทำงานของแอปในกรณีที่เกิดคำขอ BII ที่ไม่คาดคิด - การจำหน่ายมี 2 ประเภท ได้แก่ สี
INTENT
และDEEPLINK
- โดยปกติแล้ว นักพัฒนาซอฟต์แวร์สามารถแยกความแตกต่างระหว่าง
INTENT
กับ ดำเนินการตามคำสั่งซื้อDEEPLINK
รายการโดยดูที่แท็ก Intent ในshortcuts.xml
ที่ผู้ใช้ตอบสนองด้วยการเรียกไลบรารี - หากมีแท็กเทมเพลต URL อยู่ใต้แท็ก Intent ระบบจะระบุ
ว่า
DEEPLINK
ตอบสนองต่อความตั้งใจนี้ - หากเมธอด
getData()
ของ Intent ของผลการค้นหาแสดงออบเจ็กต์ที่ไม่ใช่ Null และยังหมายถึงDEEPLINK
การดำเนินการตามคำสั่งซื้อด้วย ในทำนองเดียวกัน หากgetData
แสดงผลnull
หมายความว่าเป็นการดำเนินการตามคำสั่งซื้อINTENT
- โดยปกติแล้ว นักพัฒนาซอฟต์แวร์สามารถแยกความแตกต่างระหว่าง
- สำหรับเคส
INTENT
ให้พิมพ์AppActionsFulfillmentResult
ถึงAppActionsIntentFulfillmentResult
ดึงข้อมูล Android Intent ด้วยการเรียกใช้getIntent
และดำเนินการอย่างใดอย่างหนึ่งต่อไปนี้- ยืนยันช่องต่างๆ ของ Android Intent
- ยืนยัน URI ของ Intent ที่มีการเข้าถึงผ่าน เมธอดIntent.getData.getHost
- สำหรับเคส
DEEPLINK
ให้พิมพ์AppActionsFulfillmentResult
ถึงAppActionsIntentFulfillmentResult
(เช่นเดียวกับสถานการณ์INTENT
ด้านบน) ดึงข้อมูล Android Intent ด้วยการเรียกใช้เมธอดgetIntent
และยืนยัน URL ของ Deep Link (เข้าถึงผ่านintent.getData.getHost
) - สำหรับทั้ง
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) และ ใหม่กว่า