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.

DeviceConfigurationOverride

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

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

Robolectric

Gunakan Robolectric untuk menjalankan pengujian UI berbasis Compose atau tampilan 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 berikut dari Now in Android, Robolectric dikonfigurasi untuk mengemulasikan 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 resource sistem dan 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 sungguhan tempat pengujian berinstrumen Anda berjalan. Buat spesifikasi untuk perangkat dengan berbagai ukuran layar untuk menerapkan strategi pengujian dengan pengujian tertentu harus dijalankan pada ukuran layar tertentu. Dengan menggunakan GMD dengan Continuous Integration (CI), Anda dapat memastikan bahwa pengujian yang sesuai berjalan saat diperlukan, menyediakan dan meluncurkan emulator, serta menyederhanakan penyiapan 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"
                }
            }
        }
    }
}

Anda dapat menemukan beberapa contoh GMD di project testing-samples.

Firebase Test Lab

Gunakan Firebase Test Lab (FTL), atau layanan farm perangkat serupa, untuk menjalankan pengujian di 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 pengujian yang berjalan di 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 Anda tidak perlu dijalankan di beberapa perangkat. Biasanya, Anda memfilter pengujian UI dengan menjalankan semua atau sebagian besar pengujian pada faktor bentuk ponsel dan hanya sebagian pada perangkat dengan ukuran layar yang berbeda.

Anda dapat menganotasi pengujian tertentu agar hanya dijalankan 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

Dan gunakan pada pengujian yang berbeda:

class MyTestClass {

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

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

}

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

$ ./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 Perangkat Espresso untuk melakukan tindakan pada emulator dalam pengujian menggunakan jenis pengujian berinstrumen apa pun, termasuk pengujian Espresso, Compose, atau UI Automator. Tindakan ini dapat mencakup menyetel ukuran layar atau mengubah status atau postur perangkat foldable. 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 Perangkat Espresso 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 agar Anda dapat memfilter pengujian menggunakan anotasi. Jika persyaratan yang dianotasi tidak terpenuhi, pengujian akan dilewati.

Anotasi RequiresDeviceMode

Anotasi RequiresDeviceMode dapat digunakan beberapa kali untuk menunjukkan pengujian yang hanya akan berjalan 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()))
    }
}

Anotasi RequiresDisplay

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 dilakukan 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 layar dengan setDisplaySize(), Anda tidak akan memengaruhi kepadatan perangkat, sehingga jika dimensi tidak sesuai dengan perangkat target, pengujian akan gagal dengan UnsupportedDeviceOperationException. Untuk mencegah pengujian berjalan dalam hal 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 yang 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 Pengujian Jendela

Library Pengujian Jendela 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 tersedia melalui Repositori Maven Google.

Misalnya, Anda dapat menggunakan fungsi FoldingFeature() untuk membuat FoldingFeature kustom, yang dapat Anda gunakan di 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()
    }
}

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