Library dan alat untuk menguji berbagai ukuran layar

Android menyediakan berbagai alat dan API yang dapat membantu Anda membuat pengujian untuk ukuran layar dan jendela yang berbeda.

DeviceConfigurationOverride

Composable DeviceConfigurationOverride memungkinkan Anda mengganti atribut konfigurasi untuk menguji beberapa ukuran layar dan jendela di Compose tata letak. Penggantian ForcedSize sesuai dengan tata letak apa pun di ruang yang tersedia, yang memungkinkan Anda menjalankan setiap Pengujian UI pada semua ukuran layar. Misalnya, Anda dapat menggunakan faktor bentuk ponsel berukuran kecil untuk menjalankan semua pengujian UI, termasuk pengujian UI untuk ponsel berukuran besar, perangkat foldable, tablet.

   DeviceConfigurationOverride(
        DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
    ) {
        MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
    }
Gambar 1. Menggunakan DeviceConfigurationOverride agar pas dengan tata letak tablet di dalam perangkat faktor bentuk yang lebih kecil, seperti dalam \*Now in Android*.

Selain itu, Anda dapat menggunakan composable ini untuk menyetel skala font, tema, dan properti yang mungkin ingin Anda uji pada berbagai ukuran {i>window<i} yang berbeda.

Robolectric

Menggunakan Robolectric untuk menjalankan pengujian Compose atau UI berbasis tampilan di JVM secara lokal—tidak perlu perangkat atau emulator. Anda dapat mengonfigurasi Robolectric untuk menggunakan ukuran layar tertentu, di antara properti berguna lainnya.

Dalam contoh berikut dari Now in Android, Robolectric dikonfigurasi untuk mengemulasi ukuran layar 1000x1000 dp dengan resolusi 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 { ... }

Anda juga dapat menetapkan penentu dari isi pengujian seperti yang dilakukan dalam cuplikan dari contoh Now in Android:

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

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

Perhatikan bahwa RuntimeEnvironment.setQualifiers() akan mengupdate sistem dan resource aplikasi dengan konfigurasi baru, tetapi tidak memicu tindakan apa pun pada aktivitas aktif atau komponen lainnya.

Anda dapat membaca selengkapnya di dokumentasi Konfigurasi Perangkat Robolectric.

Perangkat yang dikelola Gradle

Plugin Android Gradle Perangkat yang dikelola Gradle (GMD) memungkinkan Anda menentukan spesifikasi emulator dan perangkat aktual di mana pengujian instrumentasi Anda berjalan. Buat spesifikasi untuk perangkat dengan ukuran layar yang berbeda untuk menerapkan strategi pengujian di mana pengujian tertentu harus dijalankan pada ukuran layar tertentu. Dengan menggunakan GMD bersama Continuous Integration (CI), Anda dapat memastikan bahwa tes yang sesuai berjalan bila diperlukan, menyediakan dan meluncurkan emulator, serta menyederhanakan penyiapan CI Anda.

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"
                }
            }
        }
    }
}

Anda dapat menemukan beberapa contoh GMD di project contoh pengujian.

Firebase Test Lab

Gunakan Firebase Test Lab (FTL), atau layanan pertanian perangkat serupa, untuk menjalankan pengujian pada perangkat nyata tertentu yang mungkin tidak dapat Anda akses, seperti perangkat foldable atau tablet dengan berbagai ukuran. Firebase Test Lab adalah aplikasi berbayar dengan paket gratis. FTL juga mendukung pengujian pada emulator. Layanan ini meningkatkan keandalan dan kecepatan pengujian berinstrumen karena mereka dapat menyediakan perangkat dan emulator terlebih dahulu.

Untuk informasi tentang penggunaan FTL dengan GMD, lihat Menskalakan pengujian dengan GMD Perangkat yang dikelola Gradle.

Menguji pemfilteran dengan runner pengujian

Strategi pengujian yang optimal tidak boleh memverifikasi hal yang sama dua kali, jadi sebagian besar Pengujian UI tidak perlu dijalankan di beberapa perangkat. Biasanya, Anda memfilter UI pengujian dengan menjalankan semuanya atau sebagian besarnya pada faktor bentuk ponsel dan hanya subset pada perangkat dengan ukuran layar yang berbeda.

Anda dapat menganotasi pengujian tertentu agar dijalankan hanya dengan perangkat tertentu lalu lulus argumen ke AndroidJUnitRunner menggunakan perintah yang menjalankan pengujian.

Misalnya, Anda dapat membuat berbagai anotasi:

annotation class TestExpandedWidth
annotation class TestCompactWidth

Dan gunakan hasil tersebut pada pengujian yang berbeda:

class MyTestClass {

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

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

}

Anda kemudian dapat menggunakan android.testInstrumentationRunnerArguments.annotation saat menjalankan pengujian untuk memfilter yang tertentu. Misalnya, jika Anda menggunakan perangkat yang dikelola Gradle:

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

Jika Anda tidak menggunakan GMD dan mengelola emulator pada CI, pertama-tama pastikan bahwa emulator atau perangkat yang benar siap dan terhubung, lalu teruskan parameter ke salah satu perintah Gradle untuk menjalankan uji instrumentasi:

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

Perhatikan bahwa Perangkat Espresso (lihat bagian berikutnya) juga dapat memfilter pengujian menggunakan properti perangkat.

Perangkat Espresso

Gunakan Espresso Device untuk melakukan tindakan pada emulator dalam pengujian menggunakan jenis pengujian berinstrumen, termasuk pengujian Espresso, Compose, atau UI Automator. Tindakan ini mungkin termasuk menyetel ukuran layar atau mengganti status perangkat foldable atau postur tubuh. Misalnya, Anda dapat mengontrol emulator perangkat foldable dan menyetelnya ke mode di atas meja. Perangkat Espresso juga berisi aturan dan anotasi JUnit untuk memerlukan fitur tertentu:

@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()))
    }
}

Perhatikan bahwa Espresso Device masih dalam tahap alfa dan memiliki persyaratan:

  • Plugin Android Gradle 8.3 atau yang lebih tinggi
  • Android Emulator 33.1.10 atau yang lebih tinggi
  • Perangkat virtual Android yang menjalankan API level 24 atau yang lebih tinggi

Memfilter pengujian

Perangkat Espresso dapat membaca properti perangkat yang terhubung sehingga Anda dapat memfilter pengujian menggunakan anotasi. Jika persyaratan yang dianotasi tidak terpenuhi, dan pengujian dilewati.

AnotasiRequiredDeviceMode

Anotasi RequiresDeviceMode dapat digunakan beberapa kali untuk menunjukkan pengujian yang akan berjalan hanya jika semua nilai DeviceMode didukung di perangkat.

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()))
    }
}

AnotasiRequiredDisplay

Anotasi RequiresDisplay memungkinkan Anda menentukan lebar dan tinggi layar perangkat menggunakan class ukuran, yang menentukan bucket dimensi dengan mengikuti class ukuran jendela resmi.

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

Ubah ukuran layar

Gunakan metode setDisplaySize() untuk mengubah ukuran dimensi layar pada runtime. Gunakan metode ini bersama dengan DisplaySizeRule , yang memastikan bahwa perubahan apa pun yang dibuat selama pengujian diurungkan sebelum pengujian berikutnya.

@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.
    }
}

Jika Anda mengubah ukuran layar dengan setDisplaySize(), kepadatannya tidak akan terpengaruh pada perangkat target, jadi jika sebuah dimensi tidak sesuai dengan perangkat target, maka pengujian gagal dengan UnsupportedDeviceOperationException. Untuk mencegah pengujian dari yang sedang dijalankan dalam kasus ini, gunakan anotasi RequiresDisplay untuk memfilternya:

@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

Class StateRestorationTester digunakan untuk menguji pemulihan status untuk komponen composable tanpa membuat ulang aktivitas. Hal ini mempercepat pengujian dan lebih andal, karena pembuatan ulang aktivitas adalah proses yang kompleks dengan mekanisme sinkronisasi:

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

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

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

Library Window Testing

Library Window Testing berisi utilitas untuk membantu Anda menulis pengujian yang mengandalkan mengaktifkan atau memverifikasi fitur yang terkait dengan pengelolaan jendela, seperti aktivitas penyematan atau fitur perangkat foldable. Artefak ini tersedia melalui Repositori Maven.

Misalnya, Anda dapat menggunakan fungsi FoldingFeature() untuk membuat FoldingFeature kustom, yang dapat Anda gunakan dalam pratinjau Compose. Pada Java, gunakan fungsi createFoldingFeature().

Dalam pratinjau Compose, Anda dapat menerapkan FoldingFeature dengan cara berikut:

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

Selain itu, Anda dapat mengemulasi fitur tampilan dalam pengujian UI menggunakan Fungsi TestWindowLayoutInfo(). Contoh berikut menyimulasikan FoldingFeature dengan HALF_OPENED engsel vertikal di tengah layar, lalu memeriksa apakah adalah tata letak yang diharapkan:

Compose

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()
    }
}

View

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()))
    }
}

Anda dapat menemukan contoh lainnya di project WindowManager.

Referensi lainnya

Dokumentasi

Contoh

Codelab