เอกสารนี้จะอธิบายวิธีทำงานทั่วไปของการทดสอบอัตโนมัติโดยใช้ Espresso API
Espresso API ส่งเสริมให้ผู้เขียนการทดสอบคิดในแง่ของสิ่งที่ผู้ใช้อาจ
ขณะโต้ตอบกับแอปพลิเคชัน เช่น การค้นหาองค์ประกอบ UI และการโต้ตอบ
ร่วมกัน ในขณะเดียวกัน กรอบการทำงานก็ป้องกันไม่ให้เข้าถึงกิจกรรมโดยตรง
และมุมมองของแอปพลิเคชัน เพราะการยึดวัตถุเหล่านี้ไว้และทำงาน
อยู่นอกเธรด UI คือสาเหตุสำคัญของการทดสอบความไม่สม่ำเสมอ ดังนั้นคุณจะ
ไม่เห็นเมธอด เช่น getView()
และ getCurrentActivity()
ใน Espresso API
คุณยังสามารถดำเนินการกับข้อมูลพร็อพเพอร์ตี้ได้อย่างปลอดภัยโดยใช้คลาสย่อยของ
ViewAction
และ ViewAssertion
คอมโพเนนต์ API
ส่วนประกอบหลักของ Espresso ประกอบด้วย:
- Espresso – จุดแรกเข้าไปยังการโต้ตอบกับทิวทัศน์ (ผ่าน
onView()
และonData()
) นอกจากนี้ยังแสดง API ที่ไม่จำเป็นต้องเชื่อมโยงกับข้อมูลพร็อพเพอร์ตี้ใดๆ เช่น ด้วยชื่อpressBack()
- ViewMatchers – คอลเล็กชันของออบเจ็กต์ที่ใช้
อินเทอร์เฟซ
Matcher<? super View>
คุณสามารถส่งข้อมูลต่อไปนี้อย่างน้อย 1 รายการไปยังonView()
เพื่อค้นหาข้อมูลพร็อพเพอร์ตี้ภายในลำดับชั้นการแสดงผลปัจจุบัน - ViewActions – คอลเล็กชันของ
ViewAction
ออบเจ็กต์ที่สามารถส่งผ่านไปยัง เมธอดViewInteraction.perform()
เช่นclick()
- ViewAssertions – คอลเล็กชันของออบเจ็กต์
ViewAssertion
รายการที่สามารถ ผ่านเมธอดViewInteraction.check()
แล้ว โดยส่วนใหญ่คุณจะใช้เมธอด ตรงกับการยืนยัน ซึ่งใช้ตัวจับคู่มุมมองเพื่อยืนยันสถานะของ มุมมองที่เลือกในปัจจุบัน
ตัวอย่าง
Kotlin
// withId(R.id.my_view) is a ViewMatcher // click() is a ViewAction // matches(isDisplayed()) is a ViewAssertion onView(withId(R.id.my_view)) .perform(click()) .check(matches(isDisplayed()))
Java
// withId(R.id.my_view) is a ViewMatcher // click() is a ViewAction // matches(isDisplayed()) is a ViewAssertion onView(withId(R.id.my_view)) .perform(click()) .check(matches(isDisplayed()));
ค้นหาข้อมูลพร็อพเพอร์ตี้
ในกรณีส่วนใหญ่ เมธอด onView()
จะนำตัวจับคู่แฮมเครสต์
ที่คาดว่าจะตรงกับมุมมอง 1 รายการ และเพียงรายการเดียว — ภายในมุมมองปัจจุบัน
ลำดับชั้น เครื่องมือจับคู่มีประสิทธิภาพและจะคุ้นเคยสำหรับผู้ที่เคยใช้
ด้วย Mockito หรือ JUnit หากคุณไม่คุ้นเคยกับการจับคู่แฮมเครสต์ เรา
แนะนำให้คุณเริ่มต้นด้วยการดู
งานนำเสนอ
ข้อมูลพร็อพเพอร์ตี้ที่ต้องการมักจะมี R.id
ที่ไม่ซ้ำและตัวจับคู่ withId
แบบง่ายๆ จะ
ช่วยจำกัดการค้นหาข้อมูลพร็อพเพอร์ตี้ให้แคบลง อย่างไรก็ตาม มีหลายกรณีที่
ระบุ R.id
ไม่ได้ขณะทดสอบเวลาพัฒนา ตัวอย่างเช่น ข้อมูลพร็อพเพอร์ตี้ที่ต้องการ
อาจไม่มี R.id
หรือ R.id
ซ้ำกัน ซึ่งจะทำให้เป็นปกติ
การวัดคุมการเขียนมีความยากและซับซ้อน เนื่องจากวิธีปกติในการ
เข้าถึงมุมมองด้วย findViewById()
จะไม่ทำงาน ดังนั้นคุณจึง
ต้องการเข้าถึงสมาชิกส่วนตัวของกิจกรรมหรือส่วนย่อยที่เก็บมุมมองหรือ
ค้นหาคอนเทนเนอร์ที่มี R.id
ที่รู้จัก และไปที่เนื้อหาของคอนเทนเนอร์สำหรับ
มุมมองใดมุมมองหนึ่ง
Espresso จัดการปัญหานี้ได้อย่างสะอาดตาโดยให้คุณจำกัดมุมมองให้แคบลง
โดยใช้ออบเจ็กต์ ViewMatcher
ที่มีอยู่หรือออบเจ็กต์ที่กำหนดเอง
การค้นหามุมมองตาม R.id
นั้นทำได้ง่ายๆ เพียงเรียกใช้ onView()
:
Kotlin
onView(withId(R.id.my_view))
Java
onView(withId(R.id.my_view));
ในบางครั้ง ค่า R.id
อาจมีการแชร์กันระหว่างข้อมูลพร็อพเพอร์ตี้หลายรายการ เมื่อเกิดเหตุการณ์นี้ขึ้น
การพยายามใช้ R.id
บางอย่างจะทำให้มีข้อยกเว้น เช่น
AmbiguousViewMatcherException
ข้อความข้อยกเว้นจะแสดงข้อความ
การนำเสนอลำดับชั้นของมุมมองปัจจุบัน ซึ่งคุณสามารถค้นหาและดูได้
ข้อมูลพร็อพเพอร์ตี้ที่ตรงกับ R.id
ที่ซ้ำกัน ได้แก่
java.lang.RuntimeException: androidx.test.espresso.AmbiguousViewMatcherException This matcher matches multiple views in the hierarchy: (withId: is <123456789>) ... +----->SomeView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=false, enabled=true, selected=false, is-layout-requested=false, text=, root-is-layout-requested=false, x=0.0, y=625.0, child-count=1} ****MATCHES**** | +------>OtherView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=true, enabled=true, selected=false, is-layout-requested=false, text=Hello!, root-is-layout-requested=false, x=0.0, y=0.0, child-count=1} ****MATCHES****
เมื่อดูแอตทริบิวต์ต่างๆ ของมุมมองแล้ว คุณอาจเห็นว่า
พร็อพเพอร์ตี้ที่ระบุตัวตนได้ ในตัวอย่างข้างต้น มุมมองหนึ่งมีข้อความ
"Hello!"
คุณสามารถใช้วิธีนี้เพื่อจำกัดการค้นหาให้แคบลงโดยใช้ชุดค่าผสม
ตัวจับคู่:
Kotlin
onView(allOf(withId(R.id.my_view), withText("Hello!")))
Java
onView(allOf(withId(R.id.my_view), withText("Hello!")));
นอกจากนี้ คุณยังเลือกที่จะไม่คืนค่าการจับคู่ที่ตรงกันได้ดังนี้
Kotlin
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))
Java
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))));
โปรดดู ViewMatchers
เครื่องมือจับคู่มุมมองจาก Espresso
ข้อควรพิจารณา
- ในแอปพลิเคชันที่มีลักษณะการทำงานที่ดี มุมมองทั้งหมดที่ผู้ใช้โต้ตอบด้วยได้
ควรมีข้อความอธิบายหรือมีคำอธิบายเนื้อหา โปรดดู
การทําให้แอปเข้าถึงได้ง่ายขึ้นสําหรับสิ่งต่างๆ มากขึ้น
รายละเอียด หากไม่สามารถจำกัดขอบเขตการค้นหาโดยใช้
withText()
หรือ คุณwithContentDescription()
โปรดพิจารณาว่าข้อความนี้เป็นข้อบกพร่องในการช่วยเหลือพิเศษ - ใช้เครื่องมือจับคู่ที่สื่อความหมายซึ่งพบข้อมูลพร็อพเพอร์ตี้ที่คุณต้องการดู
สำหรับ อย่าระบุมากเกินไปเนื่องจากจะบังคับให้เฟรมเวิร์กทำงานมากกว่า
เป็นสิ่งจำเป็น ตัวอย่างเช่น หากข้อความระบุมุมมองได้อย่างไม่ซ้ำกัน คุณสามารถ
ไม่จำเป็นต้องระบุว่าให้สิทธิ์ข้อมูลพร็อพเพอร์ตี้จาก
TextView
ได้ด้วย สำหรับผู้ที่ชอบ การดูR.id
ของการดูก็น่าจะเพียงพอแล้ว - หากมุมมองเป้าหมายอยู่ใน
AdapterView
เช่นListView
GridView
หรือSpinner
- เมธอดonView()
อาจใช้ไม่ได้ ใน คุณควรใช้onData()
แทน
ดำเนินการกับข้อมูลพร็อพเพอร์ตี้
เมื่อคุณพบตัวจับคู่ที่เหมาะสมกับมุมมองเป้าหมายแล้ว คุณอาจ
ทำอินสแตนซ์ของ ViewAction
ในคำสั่งนั้นโดยใช้เมธอด
เช่น หากต้องการคลิกข้อมูลพร็อพเพอร์ตี้ ให้ทําดังนี้
Kotlin
onView(...).perform(click())
Java
onView(...).perform(click());
คุณสามารถดำเนินการได้มากกว่า 1 รายการด้วยการเรียกใช้ครั้งเดียว
Kotlin
onView(...).perform(typeText("Hello"), click())
Java
onView(...).perform(typeText("Hello"), click());
หากมุมมองที่คุณกําลังทํางานด้วยอยู่ใน ScrollView
(แนวตั้งหรือ
แนวนอน) ให้พิจารณาการดำเนินการก่อนหน้าที่กำหนดให้มุมมอง
ที่แสดง เช่น click()
และ typeText()
ด้วย scrollTo()
ช่วงเวลานี้
ตรวจสอบว่ามุมมองปรากฏขึ้นก่อนที่จะดำเนินการอย่างอื่น
Kotlin
onView(...).perform(scrollTo(), click())
Java
onView(...).perform(scrollTo(), click());
โปรดดู ViewActions
เพื่อดูการทำงานโดย Espresso
ตรวจสอบการยืนยันการดู
คุณสามารถใช้การยืนยันกับมุมมองที่เลือกในปัจจุบันด้วย check()
การยืนยันที่ใช้บ่อยที่สุดคือการยืนยัน matches()
โดยใช้
ViewMatcher
เพื่อยืนยันสถานะของมุมมองที่เลือกในปัจจุบัน
ตัวอย่างเช่น หากต้องการตรวจสอบว่ามุมมองมีข้อความ "Hello!"
หรือไม่ ให้ทำดังนี้
Kotlin
onView(...).check(matches(withText("Hello!")))
Java
onView(...).check(matches(withText("Hello!")));
หากต้องการยืนยันว่า "Hello!"
เป็นเนื้อหาของการแสดงผล การดำเนินการต่อไปนี้ถือเป็นแนวทางปฏิบัติที่ไม่เหมาะสม
Kotlin
// Don't use assertions like withText inside onView. onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()))
Java
// Don't use assertions like withText inside onView. onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));
ในทางกลับกัน หากคุณต้องการยืนยันว่าการดูที่มีข้อความ "Hello!"
เป็น
แสดงตัวได้ ตัวอย่างเช่น หลังจากมีการเปลี่ยนแปลงค่าสถานะการมองเห็น
โค้ดก็ไม่มีปัญหา
ดูการทดสอบการยืนยันอย่างง่าย
ในตัวอย่างนี้ SimpleActivity
มี Button
และ TextView
เมื่อ
คลิกปุ่มแล้ว เนื้อหาของ TextView
จะเปลี่ยนเป็น "Hello Espresso!"
วิธีทดสอบด้วย Espresso มีดังนี้
คลิกปุ่ม
ขั้นตอนแรกคือมองหาพร็อพเพอร์ตี้ที่จะช่วยค้นหาปุ่ม
ใน SimpleActivity
มี R.id
ที่ไม่ซ้ำกันตามที่คาดไว้
Kotlin
onView(withId(R.id.button_simple))
Java
onView(withId(R.id.button_simple));
วิธีดำเนินการคลิกมีดังนี้
Kotlin
onView(withId(R.id.button_simple)).perform(click())
Java
onView(withId(R.id.button_simple)).perform(click());
ยืนยันข้อความ TextView
TextView
ที่มีข้อความให้ยืนยันมี R.id
ที่ไม่ซ้ำกันด้วย:
Kotlin
onView(withId(R.id.text_simple))
Java
onView(withId(R.id.text_simple));
วิธียืนยันข้อความเนื้อหา
Kotlin
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")))
Java
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")));
ตรวจสอบการโหลดข้อมูลในมุมมองอะแดปเตอร์
AdapterView
เป็นวิดเจ็ตประเภทพิเศษที่โหลดข้อมูลแบบไดนามิกจาก
อะแดปเตอร์ ตัวอย่างที่พบบ่อยที่สุดของ AdapterView
คือ ListView
อาส
ต่างจากวิดเจ็ตแบบคงที่ เช่น LinearLayout
แต่มีเพียง
ระบบอาจโหลดรายการย่อย AdapterView
รายการลงในลำดับชั้นการแสดงผลปัจจุบัน องค์ประกอบ
การค้นหา onView()
จะไม่พบมุมมองที่ไม่ได้โหลดในขณะนี้
Espresso จัดการเรื่องนี้โดยการระบุจุดแรกเข้า onData()
แยกต่างหาก
โหลดรายการอะแดปเตอร์ที่เป็นปัญหาได้ก่อน ทำให้โฟกัสก่อน
หรือบริษัทย่อยๆ ของ Google
คำเตือน: การติดตั้งที่กำหนดเองของ
AdapterView
อาจมีปัญหาเกี่ยวกับ onData()
หากพวกเขาละเมิดสัญญาการรับค่า โดยเฉพาะ
getItem()
API ในกรณีดังกล่าว การดำเนินการที่ดีที่สุดคือ
เปลี่ยนโครงสร้างภายในโค้ดของแอปพลิเคชัน หากทำไม่ได้ คุณสามารถใช้
ตรงกับ AdapterViewProtocol
ที่กำหนดเอง สำหรับข้อมูลเพิ่มเติม โปรดดู
ให้ดูที่ค่าเริ่มต้น
คลาส AdapterViewProtocols
จาก Espresso
การทดสอบแบบง่ายของมุมมองอะแดปเตอร์
การทดสอบง่ายๆ นี้จะสาธิตวิธีใช้ onData()
SimpleActivity
มี
Spinner
กับรายการที่เป็นเครื่องดื่มประเภทกาแฟ 2-3 รายการ เมื่อ
รายการที่เลือกไว้ มี TextView
ที่เปลี่ยนเป็น "One %s a day!"
โดยที่
%s
แสดงรายการที่เลือก
เป้าหมายของการทดสอบนี้คือการเปิด Spinner
เลือกรายการที่เจาะจง และ
ตรวจสอบว่า TextView
มีรายการดังกล่าวอยู่ เนื่องจากชั้นเรียน Spinner
อิงตาม
ใน AdapterView
ขอแนะนำให้ใช้ onData()
แทน onView()
สำหรับ
การจับคู่สินค้า
เปิดการเลือกรายการ
Kotlin
onView(withId(R.id.spinner_simple)).perform(click())
Java
onView(withId(R.id.spinner_simple)).perform(click());
เลือกรายการ
สําหรับการเลือกรายการ Spinner
จะสร้าง ListView
พร้อมเนื้อหา
มุมมองนี้อาจยาวมาก และองค์ประกอบอาจไม่ได้แสดงในข้อมูลพร็อพเพอร์ตี้
ลำดับชั้น การใช้ onData()
เราจะบังคับให้องค์ประกอบที่ต้องการเข้าสู่มุมมอง
ลำดับชั้น รายการใน Spinner
เป็นสตริง เราจึงต้องการจับคู่รายการ
ที่เท่ากับสตริง "Americano"
:
Kotlin
onData(allOf(`is`(instanceOf(String::class.java)), `is`("Americano"))).perform(click())
Java
onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());
ยืนยันว่าข้อความถูกต้อง
Kotlin
onView(withId(R.id.spinnertext_simple)) .check(matches(withText(containsString("Americano"))))
Java
onView(withId(R.id.spinnertext_simple)) .check(matches(withText(containsString("Americano"))));
การแก้ไขข้อบกพร่อง
Espresso จะให้ข้อมูลการแก้ไขข้อบกพร่องที่เป็นประโยชน์เมื่อการทดสอบล้มเหลว โดยมีรายละเอียดดังนี้
การบันทึก
Espresso จะบันทึกการดำเนินการในมุมมองทั้งหมดไปยัง Logcat เช่น
ViewInteraction: Performing 'single click' action on view with text: Espresso
ดูลำดับชั้น
เอสเพรสโซพิมพ์ลำดับชั้นการแสดงผลในข้อความข้อยกเว้นเมื่อ onView()
ล้มเหลว
- หาก
onView()
ไม่พบมุมมองเป้าหมายNoMatchingViewException
จะเป็น โยนได้ คุณสามารถตรวจสอบลำดับชั้นการแสดงผลในสตริงข้อยกเว้นเพื่อวิเคราะห์ ทำไมตัวจับคู่จึงไม่ตรงกับจำนวนการดูใดๆ - หาก
onView()
พบข้อมูลพร็อพเพอร์ตี้หลายรายการที่ตรงกับตัวจับคู่ที่ระบุ โยนAmbiguousViewMatcherException
แล้ว ระบบจะพิมพ์ลำดับชั้นของมุมมองทั้งหมด การดูที่จับคู่จะมีป้ายกำกับMATCHES
:
java.lang.RuntimeException: androidx.test.espresso.AmbiguousViewMatcherException This matcher matches multiple views in the hierarchy: (withId: is <123456789>) ... +----->SomeView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=false, enabled=true, selected=false, is-layout-requested=false, text=, root-is-layout-requested=false, x=0.0, y=625.0, child-count=1} ****MATCHES**** | +------>OtherView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=true, enabled=true, selected=false, is-layout-requested=false, text=Hello!, root-is-layout-requested=false, x=0.0, y=0.0, child-count=1} ****MATCHES****
เมื่อจัดการกับลำดับชั้นของมุมมองที่ซับซ้อนหรือลักษณะการทำงานที่ไม่คาดคิดของวิดเจ็ต คุณควรใช้ มุมมองลำดับชั้นใน Android Studio สำหรับ คำอธิบาย
คำเตือนเกี่ยวกับมุมมองอะแดปเตอร์
Espresso เตือนผู้ใช้เกี่ยวกับวิดเจ็ต AdapterView
เมื่อonView()
การดำเนินการทำให้เกิดวิดเจ็ต NoMatchingViewException
และ AdapterView
ที่แสดงในลำดับชั้นการแสดงผล วิธีแก้ที่พบบ่อยที่สุดคือการใช้ onData()
ข้อความข้อยกเว้นจะรวมคำเตือนพร้อมรายการมุมมองอะแดปเตอร์
คุณอาจใช้ข้อมูลนี้เพื่อเรียกใช้ onData()
เพื่อโหลดมุมมองเป้าหมาย
แหล่งข้อมูลเพิ่มเติม
หากต้องการข้อมูลเพิ่มเติมเกี่ยวกับการใช้ Espresso ในการทดสอบ Android โปรดดู แหล่งข้อมูลต่อไปนี้
ตัวอย่าง
- CustomMatcherSample:
แสดงวิธีขยาย Espresso เพื่อให้ตรงกับคุณสมบัติคำแนะนำของออบเจ็กต์
EditText
- RecyclerViewSample:
การดำเนินการ
RecyclerView
รายการสำหรับ Espresso - (เพิ่มเติม...)