Librerie e strumenti per testare diverse dimensioni dello schermo

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

OverrideConfigurazioneDispositivo

L'elemento componibile DeviceConfigurationOverride consente di sostituire gli attributi di configurazione per testare più dimensioni di schermi e finestre nei layout di Scrivi. L'override di ForcedSize si adatta a qualsiasi layout nello spazio disponibile, consentendoti di eseguire qualsiasi test dell'interfaccia utente su qualsiasi dimensione dello schermo. Ad esempio, puoi usare un fattore di forma per smartphone di piccole dimensioni per eseguire tutti i test dell'interfaccia utente, compresi quelli per telefoni di grandi dimensioni, pieghevoli e 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 adattare il layout di un tablet a un dispositivo con fattore di forma più piccolo, come \*Ora in Android*.

Inoltre, puoi utilizzare questo componibile per impostare la scala dei caratteri, i temi e altre proprietà che potresti voler testare su diverse dimensioni di finestre.

Robolectric

Usa Robolectric per eseguire test di Compose o dell'UI basati su visualizzazioni sulla JVM a livello locale, senza bisogno di emulatori o dispositivi. Puoi configurare Robolectric per utilizzare dimensioni di schermo specifiche, oltre ad altre proprietà utili.

Nel seguente esempio di Now in Android, Robolectric è configurato per emulare una dimensione dello schermo di 1000 x 1000 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 dell'esempio Ora 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 le risorse di sistema e delle applicazioni con la nuova configurazione, ma non attiva alcuna azione sulle attività attive o su altri componenti.

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

Dispositivi gestiti da Gradle

Il plug-in Android Gradle Gradle-managed devices (GMD) consente di definire le specifiche degli emulatori e dei dispositivi reali su cui vengono eseguiti i test intrumentati. Crea specifiche per i dispositivi con dimensioni dello schermo diverse per implementare una strategia di test in cui determinati test devono essere eseguiti su determinate dimensioni dello schermo. Utilizzando GMD con integrazione continua (CI), puoi assicurarti che i test appropriati vengano eseguiti quando necessario, eseguendo il provisioning e avviando gli emulatori, semplificando la configurazione 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 i tuoi test su dispositivi reali specifici a cui potresti non avere accesso, ad esempio pieghevoli o tablet di varie dimensioni. Firebase Test Lab è un servizio a pagamento con livello senza costi. FTL supporta anche l'esecuzione di test su emulatori. Questi servizi migliorano l'affidabilità e la velocità dei test con strumentazione poiché possono eseguire il provisioning di dispositivi ed emulatori in anticipo.

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

Verifica l'applicazione dei filtri con l'esecutore del test

Una strategia di test ottimale non dovrebbe verificare la stessa cosa due volte, quindi la maggior parte dei test dell'interfaccia utente non deve essere eseguita su più dispositivi. In genere, i test UI vengono filtrati eseguendo tutti o la maggior parte dei test sul fattore di forma di uno smartphone e soltanto su un sottoinsieme su dispositivi con dimensioni dello schermo diverse.

Puoi annotare determinati test in modo che vengano eseguiti solo con determinati dispositivi e poi passare un argomento ad AndroidJUnitRunner utilizzando il comando che esegue i test.

Ad esempio, puoi creare annotazioni diverse:

annotation class TestExpandedWidth
annotation class TestCompactWidth

E utilizzali in diversi test:

class MyTestClass {

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

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

}

Puoi quindi utilizzare la proprietà android.testInstrumentationRunnerArguments.annotation quando esegui i test per filtrare quelli specifici. Ad esempio, se utilizzi 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 corretti siano pronti e connessi, quindi passa il parametro a uno dei comandi Gradle per eseguire i test instrumentati:

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

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

Dispositivo per il caffè espresso

Utilizza Dispositivo Espresso per eseguire azioni sugli emulatori nei test utilizzando qualsiasi tipo di test instrumentato, inclusi i test Espresso, Compose o di UI Automator. Queste azioni possono includere l'impostazione delle dimensioni dello schermo o l'attivazione/disattivazione degli stati o delle posizioni pieghevoli. Ad esempio, puoi controllare un emulatore pieghevole e impostarlo in modalità da tavolo. Il dispositivo Espresso contiene anche regole e annotazioni JUnit per richiedere determinate 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 Dispositivo espresso è ancora in fase alpha e ha i seguenti requisiti:

  • Plug-in Android Gradle 8.3 o versioni successive
  • Emulatore Android 33.1.10 o versioni successive
  • Dispositivo virtuale Android che esegue il livello API 24 o versioni successive

Filtra test

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

Annotazione requiredDeviceMode

L'annotazione RequiresDeviceMode può essere utilizzata più volte per indicare un test che verrà eseguito solo se tutti i valori di DeviceMode sono supportati 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 requiredDisplay

L'annotazione RequiresDisplay ti consente di specificare la larghezza e l'altezza dello schermo del dispositivo utilizzando le classi di dimensioni, che definiscono i bucket di dimensioni secondo le classi di dimensioni delle finestre ufficiali.

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

Ridimensionamento dei display

Utilizza il metodo setDisplaySize() per ridimensionare le dimensioni dello schermo in fase di esecuzione. Utilizza il metodo in combinazione con la classe DisplaySizeRule, per assicurarti che le modifiche apportate durante i test vengano annullate prima del test 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.
    }
}

Il display con setDisplaySize() non influisce sulla densità del dispositivo; pertanto, se una dimensione non rientra nel dispositivo di destinazione, il test non andrà a buon fine con un UnsupportedDeviceOperationException. Per impedire l'esecuzione dei test 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 per il ripristino dello stato

La classe StateRestorationTester viene utilizzata per testare il ripristino dello stato per i componenti componibili senza ricreare attività. Ciò rende i test più veloci e affidabili, poiché la ricreazione delle attività è un processo complesso con più 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 di test per finestre

La libreria di test delle finestre contiene utilità che consentono di scrivere test che si basano o verificano le funzionalità relative alla gestione delle finestre, come l'incorporamento delle attività o le funzionalità pieghevoli. L'elemento è disponibile tramite il repository Maven di Google.

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

In un'anteprima di Scrivi, 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à di visualizzazione nei test della UI utilizzando la funzione TestWindowLayoutInfo(). L'esempio seguente simula un FoldingFeature con una cerniera verticale HALF_OPENED al centro dello schermo, poi verifica se il layout è quello previsto:

Scrivi

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 WindowManager.

Risorse aggiuntive

Documentazione

Campioni

Codelab