ทดสอบส่วนย่อย

หัวข้อนี้อธิบายวิธีรวม API ที่ได้จากเฟรมเวิร์กในการทดสอบ ที่ใช้ประเมินพฤติกรรมของแต่ละส่วนย่อย

Fragment จะทำหน้าที่เป็นคอนเทนเนอร์ที่ใช้ซ้ำได้ภายในแอปของคุณ ซึ่งช่วยให้คุณทำสิ่งต่อไปนี้ได้ นำเสนอเลย์เอาต์อินเทอร์เฟซผู้ใช้ชุดเดียวกันในหลายๆ กิจกรรมและ และการกำหนดค่าเลย์เอาต์ทั้งหมด เนื่องจากส่วนย่อยมีความอเนกประสงค์ จึงมีความสำคัญ เพื่อมอบประสบการณ์การใช้งานที่สม่ำเสมอและประหยัดทรัพยากร ข้อควรทราบ

  • ส่วนย่อยของคุณไม่ควรขึ้นอยู่กับกิจกรรมหลักที่เจาะจงหรือ ส่วนย่อย
  • คุณไม่ควรสร้างลำดับชั้นการดูของส่วนย่อยนอกเสียจากว่าส่วนย่อย แสดงต่อผู้ใช้

ในการช่วยกำหนดเงื่อนไขสำหรับการทดสอบเหล่านี้ AndroidX ไลบรารี fragment-testing ให้บริการ FragmentScenario เพื่อสร้างส่วนย่อยและเปลี่ยนส่วน Lifecycle.State

การประกาศทรัพยากร Dependency

หากต้องการใช้ FragmentScenario ให้กำหนดอาร์ติแฟกต์ fragment-testing-manifest ใน ไฟล์ build.gradle ของแอปที่ใช้ debugImplementation และอาร์ติแฟกต์ fragment-testing ที่ใช้ androidTestImplementation ตามที่แสดงใน ตัวอย่างต่อไปนี้

Groovy

dependencies {
    def fragment_version = "1.8.5"

    debugImplementation "androidx.fragment:fragment-testing-manifest:$fragment_version"

    androidTestImplementation "androidx.fragment:fragment-testing:$fragment_version"
}

Kotlin

dependencies {
    val fragment_version = "1.8.5"

    debugImplementation("androidx.fragment:fragment-testing-manifest:$fragment_version")

    androidTestImplementation("androidx.fragment:fragment-testing:$fragment_version")
}

ตัวอย่างการทดสอบในหน้านี้ใช้การยืนยันจาก Espresso และ ไลบรารี Truth สำหรับข้อมูลเกี่ยวกับ ไลบรารีการทดสอบและการยืนยันอื่นๆ ที่มีอยู่ โปรดดู ตั้งค่าโปรเจ็กต์สำหรับ AndroidX Test

สร้างส่วนย่อย

FragmentScenario มีเมธอดต่อไปนี้สำหรับการเปิดใช้ Fragment ในการทดสอบ:

  • launchInContainer() เพื่อทดสอบอินเทอร์เฟซผู้ใช้ของ Fragment FragmentScenario แนบ ไปยังตัวควบคุมมุมมองรูทของกิจกรรม กิจกรรมนี้มีกิจกรรมอยู่ หรือว่างเปล่า
  • launch() สำหรับการทดสอบโดยไม่มีอินเทอร์เฟซผู้ใช้ของ Fragment FragmentScenario แนบส่วนย่อยประเภทนี้กับกิจกรรมเปล่าซึ่งไม่ มีมุมมองรูท

หลังจากเปิดใช้งานหนึ่งในประเภทส่วนย่อยเหล่านี้ FragmentScenario จะขับเคลื่อน ส่วนย่อยภายใต้การทดสอบเป็นสถานะที่ระบุ โดยค่าเริ่มต้น สถานะนี้คือ RESUMED แต่คุณสามารถลบล้างได้ด้วยอาร์กิวเมนต์ initialState รัฐRESUMED บ่งบอกว่าส่วนย่อยกำลังทำงานและแสดงต่อผู้ใช้ คุณสามารถประเมิน ข้อมูลเกี่ยวกับองค์ประกอบ UI โดยใช้ Espresso UI การทดสอบ

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีเปิดใช้งานส่วนย่อยโดยใช้แต่ละวิธี

ตัวอย่างlaunchInContainer()

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        // The "fragmentArgs" argument is optional.
        val fragmentArgs = bundleOf(selectedListItem to 0)
        val scenario = launchFragmentInContainer<EventFragment>(fragmentArgs)
        ...
    }
}

ตัวอย่างlaunch()

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        // The "fragmentArgs" arguments are optional.
        val fragmentArgs = bundleOf("numElements" to 0)
        val scenario = launchFragment<EventFragment>(fragmentArgs)
        ...
    }
}

ระบุทรัพยากร Dependency

หาก Fragment มีทรัพยากร Dependency คุณระบุเวอร์ชันทดสอบของ ทรัพยากร Dependency เหล่านี้ด้วยการระบุ FragmentFactory ที่กำหนดเองให้กับเมธอด launchInContainer() หรือ launch() เมธอด

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val someDependency = TestDependency()
        launchFragmentInContainer {
            EventFragment(someDependency)
        }
        ...
    }
}

หากต้องการข้อมูลเพิ่มเติมเกี่ยวกับการใช้ FragmentFactory เพื่อให้ การอ้างอิงกับส่วนย่อย โปรดดู ตัวจัดการส่วนย่อย

ขับเคลื่อนส่วนย่อยไปยังสถานะใหม่

ในการทดสอบ UI ของแอป ตามปกติแล้วการเรียกใช้ Fragment ก็เพียงพอแล้ว อยู่ระหว่างทดสอบ และเริ่มทดสอบจากสถานะ RESUMED แบบละเอียด อย่างไรก็ดี คุณอาจประเมินลักษณะการทำงานของส่วนย่อยด้วย เมื่อเปลี่ยนจากวงจรหนึ่งไปเป็นอีกวงจรหนึ่ง คุณสามารถระบุ สถานะเริ่มต้นโดยการส่งอาร์กิวเมนต์ initialState ไปยัง launchFragment*() ฟังก์ชัน

หากต้องการขับเคลื่อนส่วนย่อยไปยังสถานะวงจรอื่น ให้เรียกใช้ moveToState() เมธอดนี้รองรับสถานะต่อไปนี้เป็นอาร์กิวเมนต์: CREATED, STARTED, RESUMED และ DESTROYED วิธีนี้จำลองสถานการณ์ ส่วนที่ส่วนย่อยหรือกิจกรรมที่มีส่วนย่อยเปลี่ยนแปลง ด้วยเหตุผลใดก็ตาม

ตัวอย่างต่อไปนี้จะเปิดส่วนย่อยการทดสอบในสถานะ INITIALIZED และ จากนั้นย้ายไปยังสถานะ RESUMED:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>(
            initialState = Lifecycle.State.INITIALIZED
        )
        // EventFragment has gone through onAttach(), but not onCreate().
        // Verify the initial state.
        scenario.moveToState(Lifecycle.State.RESUMED)
        // EventFragment moves to CREATED -> STARTED -> RESUMED.
        ...
    }
}

สร้างส่วนย่อยใหม่

หากแอปของคุณกำลังทำงานบนอุปกรณ์ที่มีทรัพยากรต่ำ ระบบจะ อาจทำลายกิจกรรมที่มีส่วนย่อยของคุณ สถานการณ์นี้ กำหนดให้แอปสร้างส่วนย่อยอีกครั้งเมื่อผู้ใช้ย้อนกลับ หากต้องการจำลองสถานการณ์นี้ โปรดโทรหา recreate():

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>()
        scenario.recreate()
        ...
    }
}

FragmentScenario.recreate() ทำลายส่วนย่อยและโฮสต์ของส่วนนั้น แล้วจึงสร้างใหม่ เมื่อ คลาส FragmentScenario จะสร้างส่วนย่อยอีกครั้งภายใต้การทดสอบ ซึ่งก็คือส่วนย่อย จะกลับสู่สถานะของวงจรการใช้งานก่อนที่จะถูกทำลาย

การดำเนินการกับส่วนย่อยของ UI

หากต้องการทริกเกอร์การทำงานของ UI ในส่วนย่อยของคุณภายใต้การทดสอบ ให้ใช้ ตัวจับคู่มุมมองเอสเพรสโซ โต้ตอบกับองค์ประกอบในมุมมองของคุณ

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>()
        onView(withId(R.id.refresh)).perform(click())
        // Assert some expected behavior
        ...
    }
}

ถ้าคุณต้องการเรียกเมธอดในส่วนนั้น เช่น การตอบกลับ ไปยังตัวเลือกในเมนูตัวเลือก คุณสามารถทำได้อย่างปลอดภัยโดย อ้างอิงไปยังส่วนย่อยโดยใช้ FragmentScenario.onFragment() และการผ่านใน FragmentAction:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>()
        scenario.onFragment { fragment ->
            fragment.myInstanceMethod()
        }
    }
}

ทดสอบการทำงานของกล่องโต้ตอบ

FragmentScenario ยังรองรับการทดสอบ ส่วนย่อยของกล่องโต้ตอบ แม้ว่าส่วนย่อยของกล่องโต้ตอบ มีองค์ประกอบ UI การจัดวางจะปรากฏในหน้าต่างแยกต่างหาก มากกว่าในตัวกิจกรรมเอง ด้วยเหตุนี้ ให้ใช้ FragmentScenario.launch() เพื่อทดสอบส่วนย่อยของกล่องโต้ตอบ

ตัวอย่างต่อไปนี้จะทดสอบกระบวนการปิดกล่องโต้ตอบ

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testDismissDialogFragment() {
        // Assumes that "MyDialogFragment" extends the DialogFragment class.
        with(launchFragment<MyDialogFragment>()) {
            onFragment { fragment ->
                assertThat(fragment.dialog).isNotNull()
                assertThat(fragment.requireDialog().isShowing).isTrue()
                fragment.dismiss()
                fragment.parentFragmentManager.executePendingTransactions()
                assertThat(fragment.dialog).isNull()
            }
        }

        // Assumes that the dialog had a button
        // containing the text "Cancel".
        onView(withText("Cancel")).check(doesNotExist())
    }
}