Compose ทำงานร่วมกับเฟรมเวิร์กการทดสอบทั่วไป
ความสามารถในการทำงานร่วมกับ Espresso
ในแอปแบบไฮบริด คุณจะเห็นคอมโพเนนต์ Compose ภายในลำดับชั้นของ View และ
View ภายใน Composable ของ Compose (ผ่าน Composable ของ AndroidView)
คุณไม่จำเป็นต้องทำตามขั้นตอนพิเศษใดๆ เพื่อจับคู่หูฟังทั้ง 2 ประเภท คุณจับคู่ View กับ onView ของ Espresso และจับคู่องค์ประกอบ Compose กับ ComposeTestRule
@Test
fun androidViewInteropTest() {
// Check the initial state of a TextView that depends on a Compose state.
Espresso.onView(withText("Hello Views")).check(matches(isDisplayed()))
// Click on the Compose button that changes the state.
composeTestRule.onNodeWithText("Click here").performClick()
// Check the new value.
Espresso.onView(withText("Hello Compose")).check(matches(isDisplayed()))
}
เพิ่มความหมายที่กำหนดขอบเขตระดับ View สำหรับการทดสอบการทำงานร่วมกันของ Compose
จำกัดขอบเขตการค้นหา Compose ให้เฉพาะมุมมองที่ต้องการ
เมื่อย้ายข้อมูล UI ที่ซับซ้อนไปยัง Compose คุณอาจพบองค์ประกอบ Compose ที่เหมือนกันซึ่งซ้อนอยู่ภายใน Android View แบบเดิมหลายรายการ เช่น ภายใน RecyclerView หรือ ViewPager ในสถานการณ์เหล่านี้ การค้นหา Compose มาตรฐาน
เช่น onNodeWithText("Save") อาจล้มเหลวและแสดงข้อผิดพลาด "พบหลายโหนด"
คุณสามารถกำหนดขอบเขตการทดสอบ Compose ไปยัง Android View ที่เฉพาะเจาะจงได้โดยตรง แทนที่จะแก้ไขโค้ดเวอร์ชันที่ใช้งานจริงเพื่อแทรกแท็กทดสอบแบบไดนามิกเพื่อแยกความแตกต่างขององค์ประกอบเหล่านี้
ใช้ onRootWithViewInteraction API ในกฎการทดสอบ ฟังก์ชันนี้ยอมรับ ViewInteraction ของ Espresso ซึ่งช่วยให้คุณใช้ประโยชน์จาก Espresso เพื่อแยก View คอนเทนเนอร์ที่เฉพาะเจาะจงและดำเนินการโต้ตอบ Compose ภายในลำดับชั้นที่กำหนดขอบเขตนั้นโดยเฉพาะ
โต้ตอบกับรายการ
หากต้องการโต้ตอบกับองค์ประกอบ Compose ภายใน RecyclerView
row ที่เฉพาะเจาะจง ให้ใช้ Espresso เพื่อค้นหาแถว จากนั้นกำหนดขอบเขตการโต้ตอบ Compose ของคุณกับแถวนั้น
ซึ่งจะไม่สนใจองค์ประกอบ Compose ที่เหมือนกันในแถวอื่นๆ ทั้งหมด
@Test fun testComposeButtonInsideRecyclerViewItem() = runComposeUiTest { // Scroll to the desired position using Espresso Espresso.onView(withId(recyclerViewId)) .perform(RecyclerViewActions.scrollToPosition<MyViewHolder>(3)) // Define an Espresso ViewInteraction that uniquely identifies the row val rowView = Espresso.onView( allOf( withId(rootViewId), hasDescendant(withText("Item #3")) ) ) // Scope the Compose search strictly to that specific row View onRootWithViewInteraction(rowView) .onNode(hasText("Like")) .performClick() }
แก้ไขความคลุมเครือใน ViewPager
เมื่อมี Fragment หลายรายการที่มีเลย์เอาต์ Compose เหมือนกันอยู่ในหน่วยความจำ พร้อมกัน คุณสามารถกำหนดขอบเขตการค้นหาไปยังรหัส View รูทของ Fragment ที่เฉพาะเจาะจง เพื่อป้องกันความคลุมเครือในการจับคู่
@Test fun testComposeButtonInsideViewPagerItem() = runComposeUiTest { // Swipe to the desired page using Espresso Espresso.onView(withId(viewPagerViewId)).perform(swipeLeft()) // Identify the specific container view using Espresso val fragmentB = Espresso.onView(withId(fragmentRootViewId)) // The generic text "Save" is now unique within this view scope onRootWithViewInteraction(fragmentB) .onNode(hasText("Save")) .assertIsDisplayed() }
ความสามารถในการทำงานร่วมกับ UiAutomator
โดยค่าเริ่มต้น องค์ประกอบที่ใช้ได้จะเข้าถึงได้จาก UiAutomator โดยใช้ตัวอธิบายที่สะดวก (ข้อความที่แสดง คำอธิบายเนื้อหา ฯลฯ) เท่านั้น
หากต้องการเข้าถึง Composable ที่ใช้ Modifier.testTag คุณต้องเปิดใช้
พร็อพเพอร์ตี้เชิงความหมาย testTagsAsResourceId สำหรับ Subtree ของ Composable นั้นๆ การเปิดใช้ลักษณะการทำงานนี้มีประโยชน์สำหรับ Composable ที่ไม่มีแฮนเดิลอื่นๆ ที่ไม่ซ้ำกัน เช่น Composable ที่เลื่อนได้ (เช่น LazyColumn)
เปิดใช้พร็อพเพอร์ตี้เชิงความหมายเพียงครั้งเดียวที่ระดับสูงในลำดับชั้นของ Composable เพื่อ
ให้มั่นใจว่า Composable ที่ซ้อนกันทั้งหมดที่มี Modifier.testTag สามารถเข้าถึงได้จาก
UiAutomator
Scaffold(
// Enables for all composables in the hierarchy.
modifier = Modifier.semantics {
testTagsAsResourceId = true
}
){
// Modifier.testTag is accessible from UiAutomator for composables nested here.
LazyColumn(
modifier = Modifier.testTag("myLazyColumn")
){
// Content
}
}
Composable ใดๆ ที่มี Modifier.testTag(tag) จะเข้าถึงได้โดยใช้ By.res(resourceName) โดยใช้ tag เดียวกันกับ resourceName
val device = UiDevice.getInstance(getInstrumentation())
val lazyColumn: UiObject2 = device.findObject(By.res("myLazyColumn"))
// Some interaction with the lazyColumn.
แหล่งข้อมูลเพิ่มเติม
- ทดสอบแอปใน Android: หน้า Landing Page หลักของการทดสอบ Android จะให้มุมมองที่กว้างขึ้นเกี่ยวกับพื้นฐานและเทคนิคการทดสอบ
- หลักพื้นฐานของการทดสอบ: ดูข้อมูลเพิ่มเติม เกี่ยวกับแนวคิดหลักเบื้องหลังการทดสอบแอป Android
- การทดสอบในเครื่อง: คุณสามารถเรียกใช้การทดสอบบางอย่าง ในเครื่องบนเวิร์กสเตชันของคุณเองได้
- การทดสอบที่วัดคุม: คุณควรเรียกใช้การทดสอบที่วัดคุมด้วย กล่าวคือ การทดสอบที่ทำงานโดยตรงในอุปกรณ์
- การรวมอย่างต่อเนื่อง: การรวมอย่างต่อเนื่องช่วยให้คุณผสานรวมการทดสอบเข้ากับไปป์ไลน์การติดตั้งใช้งานได้
- ทดสอบขนาดหน้าจอต่างๆ: เนื่องจากผู้ใช้มีอุปกรณ์ให้เลือกมากมาย คุณจึงควรทดสอบขนาดหน้าจอต่างๆ
- Espresso: แม้ว่า Espresso จะออกแบบมาสำหรับ UI ที่อิงตาม View แต่ความรู้เกี่ยวกับ Espresso ก็ยังเป็นประโยชน์สำหรับบางแง่มุมของการทดสอบ Compose