Librerie e strumenti per testare diverse dimensioni dello schermo

Android offre una varietà di strumenti e API che possono aiutarti a creare test per dimensioni di schermi e finestre diverse.

Override configurazione dispositivo

Il componibile DeviceConfigurationOverride ti consente di eseguire l'override di configurazione per testare diverse dimensioni di schermo e finestra in Compose layout. L'override di ForcedSize si adatta a qualsiasi layout nello spazio disponibile, che ti consente di eseguire Test della UI su schermi di qualsiasi dimensione. Ad esempio, puoi usare un fattore di forma di uno smartphone di piccole dimensioni per eseguire tutti i test dell'interfaccia utente, compresi i test per smartphone di grandi dimensioni, pieghevoli tablet.

   DeviceConfigurationOverride(
        DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
    ) {
        MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
    }
Figura 1. Utilizzo di DeviceConfigurationOverride per adattarsi a un layout di tablet all'interno di un dispositivo con fattore di forma più piccolo, come in \*Ora su Android*.

Inoltre, puoi usare questo componibile per impostare la scala del carattere, i temi e altro che potresti voler testare su finestre di dimensioni diverse.

Robolettrico

Utilizza Robolectric per eseguire Compose o test dell'interfaccia utente basati sulle visualizzazioni sulla JVM Localmente: non sono necessari emulatori o dispositivi. Puoi configurare Robolectric per usare dimensioni dello schermo specifiche, tra le altre proprietà utili.

Nel seguente esempio di Now in Android, Robolectric è configurato per emulare uno schermo di 1000x1000 dp con una risoluzione di 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 { ... }

Puoi anche impostare i qualificatori dal corpo del test come fatto in questo snippet da l'esempio di Now in Android:

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

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

Tieni presente che RuntimeEnvironment.setQualifiers() aggiorna il sistema e le risorse dell'applicazione con la nuova configurazione, ma non attiva alcuna azione su attività attive o su altri componenti.

Per saperne di più, consulta la documentazione sulla configurazione del dispositivo Robolectric.

Dispositivi gestiti da Gradle

Plug-in Android Gradle per dispositivi gestiti da Gradle (GMD) consente di definire le specifiche degli emulatori e dei dispositivi reali in cui vengono eseguiti i test strumentati. Crea specifiche per i dispositivi con schermi di dimensioni diverse per implementare una strategia di test in cui determinati test su schermi di determinate dimensioni. Utilizzando GMD con l'integrazione continua. (CI), puoi assicurarti che vengano eseguiti i test appropriati quando necessario, il provisioning e l'avvio di emulatori, oltre a semplificare la configurazione di 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"
                }
            }
        }
    }
}

Puoi trovare diversi esempi di GMD nel progetto testing-samples.

Firebase Test Lab

Utilizza Firebase Test Lab (FTL) o un servizio di device farm simile per eseguire il tuo su dispositivi reali specifici a cui potresti non avere accesso, come pieghevoli o tablet di varie dimensioni. Firebase Test Lab è un programma a pagamento con un Livello senza costi. FTL supporta anche l'esecuzione di test sugli emulatori. Questi servizi migliorano l'affidabilità e la velocità dei test strumentati perché possono eseguire il provisioning dei dispositivi e degli emulatori in anticipo.

Per informazioni sull'utilizzo di FTL con GMD, vedi Scalare i test con dispositivi gestiti da Gradle.

Applica il filtro di test con l'esecutore del test

Una strategia di test ottimale non dovrebbe verificare la stessa cosa due volte, quindi la maggior parte dei I test dell'interfaccia utente non devono essere eseguiti su più dispositivi. Di solito, filtri la UI eseguendo tutti i test o la maggior parte di essi su un fattore di forma telefonico e solo un sottoinsieme dispositivi con schermi di dimensioni diverse.

Puoi annotare determinati test in modo che vengano eseguiti solo su determinati dispositivi e poi superarli un argomento a AndroidJUnitRunner utilizzando il comando che esegue test.

Ad esempio, puoi creare annotazioni diverse:

annotation class TestExpandedWidth
annotation class TestCompactWidth

Usali in diversi test:

class MyTestClass {

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

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

}

Dopodiché potrai usare android.testInstrumentationRunnerArguments.annotation proprietà durante l'esecuzione dei test per filtrare quelli specifici. Ad esempio, se stai usando i dispositivi gestiti da Gradle:

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

Se non utilizzi GMD e gestisci emulatori su CI, assicurati prima che l'emulatore o il dispositivo corretto sia pronto e connesso, quindi passi il parametro a uno dei comandi Gradle per eseguire test strumentati:

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

Tieni presente che il dispositivo Espresso (vedi sezione successiva) può anche filtrare i test utilizzando proprietà del dispositivo.

Dispositivo per caffè espresso

Utilizza Dispositivo Espresso per eseguire azioni sugli emulatori nei test utilizzando qualsiasi tipo di test con strumenti, tra cui test di Espresso, Compose o UI Automator. Queste azioni potrebbero includere l'impostazione delle dimensioni dello schermo o l'attivazione/disattivazione degli stati pieghevoli. o posture. Ad esempio, puoi controllare un emulatore pieghevole e impostarlo modalità da tavolo. Il dispositivo Espresso contiene anche regole JUnit e annotazioni per richiedono alcune funzionalità:

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

Tieni presente che il dispositivo Espresso è ancora in fase alpha e presenta quanto segue requisiti:

  • Plug-in Android Gradle 8.3 o versioni successive
  • Emulatore Android 33.1.10 o versioni successive
  • Dispositivo virtuale Android con livello API 24 o successivo

Filtra test

Il dispositivo Espresso può leggere le proprietà dei dispositivi connessi per consentirti di filtra i test usando le annotazioni. Se i requisiti annotati non vengono soddisfatti, i test vengono ignorati.

Annotazione Richiede DeviceMode

L'annotazione RequiresDeviceMode può essere utilizzata più volte per indicare un test che verrà eseguito solo se sono supportati tutti i valori DeviceMode sul dispositivo.

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

Annotazione Richiedi Display

L'annotazione RequiresDisplay ti consente di specificare la larghezza e l'altezza del schermo del dispositivo tramite le classi di dimensioni, che definiscono i bucket di dimensioni seguendo le classi di dimensioni della finestra ufficiali.

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

Ridimensiona i display

Utilizza il metodo setDisplaySize() per ridimensionare le dimensioni dello schermo in fase di runtime. Utilizza il metodo in combinazione con la sezione DisplaySizeRule che assicura che tutte le modifiche apportate durante i test vengano annullate prima successivo.

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

Quando ridimensiona un display con setDisplaySize(), la densità non viene interessata del dispositivo. Se una dimensione non rientra nel dispositivo target, il test con un errore UnsupportedDeviceOperationException. Per impedire che i test vengano eseguiti in questo caso, utilizza l'annotazione RequiresDisplay per escluderli:

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

Tester del ripristino dello stato

La classe StateRestorationTester viene utilizzata per testare il ripristino dello stato per i componenti componibili senza ricreare le attività. In questo modo i test sono più veloci e più affidabile, poiché la ricreazione dell'attività è un processo complesso con meccanismi di sincronizzazione:

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

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

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

Libreria per i test delle finestre

La libreria Window Testing contiene utilità che consentono di scrivere test che si basano attivare o verificare funzionalità relative alla gestione delle finestre, come attività incorporamento o funzionalità pieghevoli. Il file è disponibile tramite la Repository Maven.

Ad esempio, puoi utilizzare la funzione FoldingFeature() per generare una FoldingFeature personalizzato, che puoi utilizzare nelle anteprime di Compose. In Java, utilizza la funzione createFoldingFeature().

In un'anteprima di Compose, potresti implementare FoldingFeature nel seguente modo:

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

Inoltre, puoi emulare le funzionalità del display nei test dell'interfaccia utente utilizzando TestWindowLayoutInfo(). L'esempio seguente simula FoldingFeature con un HALF_OPENED la cerniera verticale al centro dello schermo, quindi controlla se è quello previsto:

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

Visualizzazioni

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

Puoi trovare altri esempi nel progetto WindowsManager.

Risorse aggiuntive

Documentazione

Campioni

Codelab