ไลบรารีและเครื่องมือเพื่อทดสอบหน้าจอขนาดต่างๆ

Android มีเครื่องมือและ API มากมายที่สามารถช่วยคุณสร้างการทดสอบสำหรับ หน้าจอและหน้าต่างขนาดต่างๆ

ลบล้างการกำหนดค่าอุปกรณ์

Composable ของ DeviceConfigurationOverride ช่วยให้คุณลบล้าง แอตทริบิวต์การกำหนดค่าเพื่อทดสอบหน้าจอและหน้าต่างหลายขนาดใน Compose เลย์เอาต์ การลบล้าง ForcedSize จะพอดีกับเลย์เอาต์ในพื้นที่ที่มีอยู่ ซึ่งให้คุณเรียกใช้ ทดสอบ UI บนหน้าจอทุกขนาด เช่น คุณสามารถใช้รูปแบบของอุปกรณ์ขนาดเล็กได้ ทำการทดสอบ UI ทั้งหมด ซึ่งรวมถึงการทดสอบ UI สำหรับมือถือขนาดใหญ่ อุปกรณ์แบบพับได้ และ แท็บเล็ต

   DeviceConfigurationOverride(
        DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
    ) {
        MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
    }
รูปที่ 1 การใช้ DeviceConfigurationOverride เพื่อปรับขนาดเลย์เอาต์แท็บเล็ตให้อยู่ภายในอุปกรณ์ที่มีรูปแบบของอุปกรณ์ขนาดเล็ก เช่น \*Now ใน Android*

นอกจากนี้ คุณยังสามารถใช้ Composable นี้เพื่อตั้งค่าการปรับขนาดแบบอักษร ธีม และอื่นๆ ที่คุณอาจต้องการทดสอบกับหน้าต่างขนาดต่างๆ

โรโบเลตริก

ใช้ Robolectric เพื่อเรียกใช้การทดสอบ UI แบบเขียนหรือแบบอิงตามการดูใน JVM ในเครื่อง - ไม่ต้องใช้อุปกรณ์หรือโปรแกรมจำลอง คุณสามารถกำหนดค่า ใช้ขนาดหน้าจอที่เจาะจง รวมถึงคุณสมบัติอื่นๆ ที่เป็นประโยชน์

ในตัวอย่างต่อไปนี้จาก Now in Android มีการกำหนดค่า Robolectric วิธีจำลองขนาดหน้าจอ 1000x1000 dp ที่มีความละเอียด 480 dpi

@RunWith(RobolectricTestRunner::class)
// Configure Robolectric to use a very large screen size that can fit all of the test sizes.
// This allows enough room to render the content under test without clipping or scaling.
@Config(qualifiers = "w1000dp-h1000dp-480dpi")
class NiaAppScreenSizesScreenshotTests { ... }

คุณยังกำหนดตัวระบุจากเนื้อหาทดสอบตามที่ทำในข้อมูลโค้ดนี้ได้ด้วย ตัวอย่าง Now in Android

val (width, height, dpi) = ...

// Set qualifiers from specs.
RuntimeEnvironment.setQualifiers("w${width}dp-h${height}dp-${dpi}dpi")

โปรดทราบว่า RuntimeEnvironment.setQualifiers() จะอัปเดตระบบและ ทรัพยากรของแอปพลิเคชันที่มีการกำหนดค่าใหม่ แต่ไม่ทริกเกอร์การดำเนินการใดๆ กิจกรรมที่ใช้งานอยู่หรือคอมโพเนนต์อื่นๆ

อ่านข้อมูลเพิ่มเติมได้ในเอกสารประกอบการกำหนดค่าอุปกรณ์ของ Robolectric

อุปกรณ์ที่จัดการโดย Gradle

อุปกรณ์ที่จัดการโดย Gradle (GMD) สำหรับ Android Gradle จะช่วยให้คุณกำหนดข้อมูลจำเพาะของโปรแกรมจำลองและอุปกรณ์จริง การทดสอบที่มีการวัดคุม สร้างข้อกำหนดเฉพาะสำหรับอุปกรณ์ที่มี หน้าจอขนาดต่างๆ กันเพื่อนำกลยุทธ์การทดสอบไปใช้ โดยการทดสอบบางอย่างจะต้อง แสดงบนหน้าจอบางขนาดได้ โดยการใช้ GMD กับการผสานรวมแบบต่อเนื่อง (CI) คุณสามารถมั่นใจได้ว่าการทดสอบที่เหมาะสมจะทำงาน เมื่อจำเป็น การจัดสรรและการเปิดใช้โปรแกรมจำลอง รวมถึงลดความซับซ้อนในการตั้งค่า CI

android {
    testOptions {
        managedDevices {
            devices {
                // Run with ./gradlew nexusOneApi30DebugAndroidTest.
                nexusOneApi30(com.android.build.api.dsl.ManagedVirtualDevice) {
                    device = "Nexus One"
                    apiLevel = 30
                    // Use the AOSP ATD image for better emulator performance
                    systemImageSource = "aosp-atd"
                }
                // Run with ./gradlew  foldApi34DebugAndroidTest.
                foldApi34(com.android.build.api.dsl.ManagedVirtualDevice) {
                    device = "Pixel Fold"
                    apiLevel = 34
                    systemImageSource = "aosp-atd"
                }
            }
        }
    }
}

คุณสามารถค้นหาตัวอย่างของ GMD ได้หลายรายการในโครงการ testing-ตัวอย่าง

Firebase Test Lab

ใช้ Firebase Test Lab (FTL) หรือบริการอุปกรณ์ฟาร์มที่คล้ายกันเพื่อเรียกใช้ การทดสอบในอุปกรณ์จริงบางอย่างที่คุณอาจเข้าถึงไม่ได้ เช่น อุปกรณ์แบบพับได้หรือแท็บเล็ตขนาดต่างๆ Firebase Test Lab เป็นผลิตภัณฑ์แบบมีค่าใช้จ่าย ด้วยรุ่นฟรี FTL ยังรองรับการเรียกใช้การทดสอบในโปรแกรมจำลองด้วย บริการเหล่านี้ช่วยเพิ่มความน่าเชื่อถือและความเร็วในการทดสอบด้วยเครื่องมือ เนื่องจาก ก็สามารถจัดสรรอุปกรณ์และโปรแกรมจำลองล่วงหน้าได้

สำหรับข้อมูลเกี่ยวกับการใช้ FTL กับ GMD โปรดดู ปรับขนาดการทดสอบด้วย อุปกรณ์ที่จัดการโดย Gradle

ทดสอบการกรองด้วยตัวดำเนินการทดสอบ

กลยุทธ์การทดสอบที่ดีที่สุด ไม่ควรยืนยันสิ่งเดียวกัน 2 ครั้ง ดังนั้น การทดสอบ UI ไม่จำเป็นต้องทำงานบนอุปกรณ์หลายเครื่อง โดยทั่วไป คุณสามารถกรอง UI โดยทำการทดสอบทั้งหมดหรือส่วนใหญ่ในรูปแบบของอุปกรณ์โทรศัพท์ และเปิดเฉพาะชุดย่อยใน อุปกรณ์ที่มีหน้าจอหลายขนาด

คุณสามารถใส่คำอธิบายประกอบการทดสอบบางอย่างให้เรียกใช้เฉพาะกับบางอุปกรณ์แล้วผ่านการตรวจสอบ อาร์กิวเมนต์ไปยัง AndroidJUnitRunner โดยใช้คำสั่งที่เรียกใช้ การทดสอบ

ตัวอย่างเช่น คุณสามารถสร้างคำอธิบายประกอบที่แตกต่างกันได้ ดังนี้

annotation class TestExpandedWidth
annotation class TestCompactWidth

และใช้ในการทดสอบแบบต่างๆ ดังนี้

class MyTestClass {

    @Test
    @TestExpandedWidth
    fun myExample_worksOnTablet() {
        ...
    }

    @Test
    @TestCompactWidth
    fun myExample_worksOnPortraitPhone() {
        ...
    }

}

จากนั้นคุณสามารถใช้ android.testInstrumentationRunnerArguments.annotation ขณะทำการทดสอบเพื่อกรองบางรายการ ตัวอย่างเช่น หากคุณ โดยใช้อุปกรณ์ที่จัดการโดย Gradle

$ ./gradlew pixelTabletApi30DebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'

หากคุณไม่ได้ใช้ GMD และจัดการโปรแกรมจำลองใน CI ก่อนอื่นต้องตรวจสอบให้แน่ใจว่า โปรแกรมจำลองหรืออุปกรณ์ที่ถูกต้องพร้อมใช้งานและเชื่อมต่อแล้ว จากนั้นส่งพารามิเตอร์ กับคำสั่ง Gradle อย่างใดอย่างหนึ่งเพื่อเรียกใช้การทดสอบแบบมีเครื่องควบคุม

$ ./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'

โปรดทราบว่าอุปกรณ์ Espresso (ดูส่วนถัดไป) ยังสามารถกรองการทดสอบโดยใช้ พร็อพเพอร์ตี้ของอุปกรณ์

อุปกรณ์เอสเพรสโซ

ใช้อุปกรณ์ Espresso เพื่อดำเนินการกับโปรแกรมจำลองในการทดสอบโดยใช้ ประเภทการทดสอบที่มีการวัดคุม ได้แก่ การทดสอบ Espresso, Compose หรือ UI Automator การดำเนินการเหล่านี้อาจรวมถึงการตั้งค่าขนาดหน้าจอหรือการสลับสถานะแบบพับได้ หรือท่าทาง ตัวอย่างเช่น คุณสามารถควบคุมโปรแกรมจำลองแบบพับได้และตั้งค่าเป็น โหมดบนโต๊ะ อุปกรณ์ Espresso ยังมีกฎ JUnit และคำอธิบายประกอบสำหรับ ต้องการคุณลักษณะบางอย่าง:

@RunWith(AndroidJUnit4::class)
class OnDeviceTest {

    @get:Rule(order=1) val activityScenarioRule = activityScenarioRule<MainActivity>()

    @get:Rule(order=2) val screenOrientationRule: ScreenOrientationRule =
        ScreenOrientationRule(ScreenOrientation.PORTRAIT)

    @Test
    fun tabletopMode_playerIsDisplayed() {
        // Set the device to tabletop mode.
        onDevice().setTabletopMode()
        onView(withId(R.id.player)).check(matches(isDisplayed()))
    }
}

โปรดทราบว่าอุปกรณ์ Espresso ยังอยู่ในขั้นอัลฟ่าและมีคุณสมบัติดังต่อไปนี้ ข้อกำหนด

  • ปลั๊กอิน Android Gradle 8.3 ขึ้นไป
  • โปรแกรมจำลอง Android 33.1.10 ขึ้นไป
  • อุปกรณ์เสมือน Android ที่ใช้ API ระดับ 24 ขึ้นไป

กรองการทดสอบ

อุปกรณ์ Espresso สามารถอ่านคุณสมบัติของอุปกรณ์ที่เชื่อมต่อเพื่อให้คุณทำสิ่งต่อไปนี้ได้ กรองการทดสอบโดยใช้คำอธิบายประกอบ หากไม่เป็นไปตามข้อกำหนดที่มีคำอธิบายประกอบ ระบบจะข้ามการทดสอบ

ต้องใช้คำอธิบายประกอบ DeviceMode

คุณใช้คำอธิบายประกอบ RequiresDeviceMode ได้หลายครั้งเพื่อระบุ การทดสอบที่จะทํางานต่อเมื่อระบบรองรับค่า DeviceMode ทั้งหมด ในอุปกรณ์

class OnDeviceTest {
    ...
    @Test
    @RequiresDeviceMode(TABLETOP)
    @RequiresDeviceMode(BOOK)
    fun tabletopMode_playerIdDisplayed() {
        // Set the device to tabletop mode.
        onDevice().setTabletopMode()
        onView(withId(R.id.player)).check(matches(isDisplayed()))
    }
}

ต้องแสดงข้อมูลคำอธิบายประกอบ

คำอธิบายประกอบ RequiresDisplay ช่วยให้คุณระบุความกว้างและความสูงของ หน้าจออุปกรณ์โดยใช้คลาสของขนาด ซึ่งกำหนดที่เก็บข้อมูลมิติข้อมูล ตามคลาสขนาดหน้าต่างอย่างเป็นทางการ

class OnDeviceTest {
    ...
    @Test
    @RequiresDisplay(EXPANDED, COMPACT)
    fun myScreen_expandedWidthCompactHeight() {
        ...
    }
}

ปรับขนาดจอแสดงผล

ใช้เมธอด setDisplaySize() เพื่อปรับขนาดของหน้าจอ ขณะรันไทม์ ใช้วิธีการร่วมกับ DisplaySizeRule ซึ่งจะช่วยรับประกันว่าการเปลี่ยนแปลงใดๆ ที่เกิดขึ้นระหว่างการทดสอบจะถูกยกเลิกก่อน การทดสอบถัดไป

@RunWith(AndroidJUnit4::class)
class ResizeDisplayTest {

    @get:Rule(order = 1) val activityScenarioRule = activityScenarioRule<MainActivity>()

    // Test rule for restoring device to its starting display size when a test case finishes.
    @get:Rule(order = 2) val displaySizeRule: DisplaySizeRule = DisplaySizeRule()

    @Test
    fun resizeWindow_compact() {
        onDevice().setDisplaySize(
            widthSizeClass = WidthSizeClass.COMPACT,
            heightSizeClass = HeightSizeClass.COMPACT
        )
        // Verify visual attributes or state restoration.
    }
}

เมื่อปรับขนาดจอแสดงผลด้วย setDisplaySize() จะไม่ส่งผลต่อความหนาแน่น ของอุปกรณ์ ดังนั้นหากขนาดไม่พอดีกับอุปกรณ์เป้าหมาย การทดสอบ ไม่สำเร็จด้วย UnsupportedDeviceOperationException เพื่อป้องกันการทดสอบจาก ในกรณีนี้ ให้ใช้คำอธิบายประกอบ RequiresDisplay เพื่อกรองออก

@RunWith(AndroidJUnit4::class)
class ResizeDisplayTest {

    @get:Rule(order = 1) var activityScenarioRule = activityScenarioRule<MainActivity>()

    // Test rule for restoring device to its starting display size when a test case finishes.
    @get:Rule(order = 2) var displaySizeRule: DisplaySizeRule = DisplaySizeRule()

    /**
     * Setting the display size to EXPANDED would fail in small devices, so the [RequiresDisplay]
     * annotation prevents this test from being run on devices outside the EXPANDED buckets.
     */
    @RequiresDisplay(
        widthSizeClass = WidthSizeClassEnum.EXPANDED,
        heightSizeClass = HeightSizeClassEnum.EXPANDED
    )
    @Test
    fun resizeWindow_expanded() {
        onDevice().setDisplaySize(
            widthSizeClass = WidthSizeClass.EXPANDED,
            heightSizeClass = HeightSizeClass.EXPANDED
        )
        // Verify visual attributes or state restoration.
    }
}

StateRestorationTester

คลาส StateRestorationTester ใช้เพื่อทดสอบการกู้คืนสถานะ สำหรับคอมโพเนนต์ที่ประกอบกันได้โดยไม่มีการสร้างกิจกรรมใหม่ วิธีนี้ทำให้การทดสอบรวดเร็วขึ้น และน่าเชื่อถือมากขึ้น เนื่องจากกิจกรรมสันทนาการ เป็นกระบวนการที่ซับซ้อนและ กลไกการซิงค์:

@Test
fun compactDevice_selectedEmailEmailRetained_afterConfigChange() {
    val stateRestorationTester = StateRestorationTester(composeTestRule)

    // Set content through the StateRestorationTester object.
    stateRestorationTester.setContent {
        MyApp()
    }

    // Simulate a config change.
    stateRestorationTester.emulateSavedInstanceStateRestore()
}

ไลบรารีการทดสอบหน้าต่าง

ไลบรารีการทดสอบ Window มียูทิลิตีที่จะช่วยคุณเขียนการทดสอบที่อาศัย เปิดหรือยืนยันฟีเจอร์ที่เกี่ยวข้องกับการจัดการหน้าต่าง เช่น กิจกรรม การฝังหรือฟีเจอร์แบบพับได้ อาร์ติแฟกต์พร้อมใช้งานผ่าน ที่เก็บ Maven

ตัวอย่างเช่น คุณสามารถใช้ฟังก์ชัน FoldingFeature() เพื่อสร้าง FoldingFeature ที่กำหนดเอง ซึ่งคุณสามารถใช้ในตัวอย่างการเขียนได้ ใน Java ใช้ฟังก์ชัน createFoldingFeature()

ในตัวอย่าง Compose คุณอาจใช้ FoldingFeature ได้ด้วยวิธีต่อไปนี้

@Preview(showBackground = true, widthDp = 480, heightDp = 480)
@Composable private fun FoldablePreview() =
    MyApplicationTheme {
        ExampleScreen(
            displayFeatures = listOf(FoldingFeature(Rect(0, 240, 480, 240)))
        )
 }

นอกจากนี้ คุณสามารถจำลองฟีเจอร์การแสดงผลในการทดสอบ UI โดยใช้ TestWindowLayoutInfo() ตัวอย่างต่อไปนี้จำลอง FoldingFeature ที่มี HALF_OPENED บานพับแนวตั้งที่กึ่งกลางของหน้าจอ จากนั้นตรวจสอบว่า คือเค้าโครงที่คาดไว้:

เขียน

import androidx.window.layout.FoldingFeature.Orientation.Companion.VERTICAL
import androidx.window.layout.FoldingFeature.State.Companion.HALF_OPENED
import androidx.window.testing.layout.FoldingFeature
import androidx.window.testing.layout.TestWindowLayoutInfo
import androidx.window.testing.layout.WindowLayoutInfoPublisherRule

@RunWith(AndroidJUnit4::class)
class MediaControlsFoldingFeatureTest {

    @get:Rule(order=1)
    val composeTestRule = createAndroidComposeRule<ComponentActivity>()

    @get:Rule(order=2)
    val windowLayoutInfoPublisherRule = WindowLayoutInfoPublisherRule()

    @Test
    fun foldedWithHinge_foldableUiDisplayed() {
        composeTestRule.setContent {
            MediaPlayerScreen()
        }

        val hinge = FoldingFeature(
            activity = composeTestRule.activity,
            state = HALF_OPENED,
            orientation = VERTICAL,
            size = 2
        )

        val expected = TestWindowLayoutInfo(listOf(hinge))
        windowLayoutInfoPublisherRule.overrideWindowLayoutInfo(expected)

        composeTestRule.waitForIdle()

        // Verify that the folding feature is detected and media controls shown.
        composeTestRule.onNodeWithTag("MEDIA_CONTROLS").assertExists()
    }
}

ยอดดู

import androidx.window.layout.FoldingFeature.Orientation
import androidx.window.layout.FoldingFeature.State
import androidx.window.testing.layout.FoldingFeature
import androidx.window.testing.layout.TestWindowLayoutInfo
import androidx.window.testing.layout.WindowLayoutInfoPublisherRule

@RunWith(AndroidJUnit4::class)
class MediaControlsFoldingFeatureTest {

    @get:Rule(order=1)
    val activityRule = ActivityScenarioRule(MediaPlayerActivity::class.java)

    @get:Rule(order=2)
    val windowLayoutInfoPublisherRule = WindowLayoutInfoPublisherRule()

    @Test
    fun foldedWithHinge_foldableUiDisplayed() {
        activityRule.scenario.onActivity { activity ->
            val feature = FoldingFeature(
                activity = activity,
                state = State.HALF_OPENED,
                orientation = Orientation.VERTICAL)
            val expected = TestWindowLayoutInfo(listOf(feature))
            windowLayoutInfoPublisherRule.overrideWindowLayoutInfo(expected)
        }

        // Verify that the folding feature is detected and media controls shown.
        onView(withId(R.id.media_controls)).check(matches(isDisplayed()))
    }
}

คุณสามารถค้นหาตัวอย่างเพิ่มเติมได้ในโปรเจ็กต์ WindowManager

แหล่งข้อมูลเพิ่มเติม

เอกสารประกอบ

ตัวอย่าง

Codelab