Library dan alat untuk menguji berbagai ukuran layar

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

{i>DeviceConfigurationOverride<i}

Composable DeviceConfigurationOverride memungkinkan Anda mengganti atribut konfigurasi untuk menguji beberapa ukuran layar dan jendela dalam tata letak Compose. Penggantian ForcedSize cocok dengan tata letak apa pun di ruang yang tersedia, yang memungkinkan Anda menjalankan pengujian UI pada semua ukuran layar. Misalnya, Anda dapat menggunakan faktor bentuk ponsel kecil untuk menjalankan semua pengujian UI, termasuk pengujian UI untuk ponsel besar, perangkat foldable, dan 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 sesuai dengan tata letak tablet dalam perangkat faktor bentuk yang lebih kecil, seperti di \*Now in Android*.

Selain itu, Anda dapat menggunakan composable ini untuk menetapkan skala font, tema, dan properti lain yang mungkin ingin Anda uji pada berbagai ukuran jendela.

Robolectric

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

Dalam contoh dari Now in Android berikut, 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 ini 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() 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 sebenarnya tempat pengujian berinstrumen dijalankan. Buat spesifikasi untuk perangkat dengan ukuran layar yang berbeda guna mengimplementasikan strategi pengujian saat pengujian tertentu harus dijalankan pada ukuran layar tertentu. Dengan menggunakan GMD bersama Continuous Integration (CI), Anda dapat memastikan bahwa pengujian yang tepat berjalan saat 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 dalam project testing-samples.

Firebase Test Lab

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

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

Menguji pemfilteran dengan runner pengujian

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

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

Misalnya, Anda dapat membuat anotasi yang berbeda:

annotation class TestExpandedWidth
annotation class TestCompactWidth

Gunakan keduanya pada pengujian yang berbeda:

class MyTestClass {

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

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

}

Selanjutnya, Anda dapat menggunakan properti android.testInstrumentationRunnerArguments.annotation saat menjalankan pengujian untuk memfilter properti 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, pastikan terlebih dahulu bahwa emulator atau perangkat yang benar sudah siap dan terhubung, lalu teruskan parameter ini ke salah satu perintah Gradle untuk menjalankan pengujian berinstrumen:

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

Perlu diperhatikan 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 semua jenis pengujian berinstrumen, termasuk pengujian Espresso, Compose, atau UI Automator. Tindakan ini dapat termasuk menyetel ukuran layar atau mengalihkan status atau postur perangkat foldable. Misalnya, Anda dapat mengontrol emulator perangkat foldable dan menetapkannya 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()))
    }
}

Perlu diperhatikan bahwa Espresso Device masih dalam tahap alfa dan memiliki persyaratan berikut:

  • 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 untuk memungkinkan Anda memfilter pengujian menggunakan anotasi. Jika persyaratan yang dianotasi tidak terpenuhi, pengujian akan dilewati.

Anotasi MembutuhkanDeviceMode

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

Membutuhkan anotasi Display

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

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

Mengubah ukuran layar

Gunakan metode setDisplaySize() untuk mengubah ukuran dimensi layar saat runtime. Gunakan metode ini bersama dengan class DisplaySizeRule, yang memastikan bahwa setiap perubahan 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.
    }
}

Saat mengubah ukuran tampilan dengan setDisplaySize(), Anda tidak memengaruhi kepadatan perangkat. Jadi, jika dimensi tidak muat dalam perangkat target, pengujian akan gagal dengan UnsupportedDeviceOperationException. Untuk mencegah pengujian 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 membuat pengujian lebih cepat dan lebih andal, karena pembuatan ulang aktivitas adalah proses kompleks dengan beberapa 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 atau memverifikasi fitur yang terkait dengan pengelolaan jendela, seperti penyematan aktivitas atau fitur perangkat foldable. Artefak ini tersedia melalui Repositori Maven Google.

Misalnya, Anda dapat menggunakan fungsi FoldingFeature() untuk membuat FoldingFeature kustom, yang dapat digunakan dalam pratinjau Compose. Di 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 engsel vertikal HALF_OPENED di tengah layar, lalu memeriksa apakah tata letak sesuai dengan 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()
    }
}

Penayangan

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 tambahan

Dokumentasi

Contoh

Codelab