Android fornisce una serie di strumenti e API che possono aiutarti a creare test per diverse dimensioni di schermi e finestre.
DeviceConfigurationOverride
Il componente componibile DeviceConfigurationOverride ti consente di sostituire
gli attributi di configurazione per testare più dimensioni di schermi e finestre nei layout di Compose. La sostituzione ForcedSize adatta qualsiasi layout allo spazio disponibile,
il che ti consente di eseguire qualsiasi
test dell'interfaccia utente su schermi di qualsiasi dimensione. Ad esempio, puoi utilizzare un fattore di forma di uno smartphone di piccole dimensioni per eseguire tutti i test dell'interfaccia utente, inclusi quelli per smartphone 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.
}
Inoltre, puoi utilizzare questo componente componibile per impostare la scalabilità dei caratteri, i temi e altre proprietà che potresti voler testare su finestre di dimensioni diverse.
Robolectric
Utilizza Robolectric per eseguire test dell'interfaccia utente basati su Compose o su visualizzazioni sulla JVM in locale, senza la necessità di dispositivi o emulatori. Puoi configurare Robolectric in modo che utilizzi dimensioni dello schermo specifiche, tra le altre proprietà utili.
@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 mostrato in questo snippet da l Now in Android esempio:
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 dell'applicazione con la nuova configurazione, ma non attiva alcuna azione su attività attive o altri componenti.
Per saperne di più, consulta la documentazione relativa alla configurazione dei dispositivi Device Configuration Robolectric.
Dispositivi gestiti da Gradle
Il plug-in Android per Gradle per i dispositivi gestiti da Gradle (GMD) ti consente di definire le specifiche degli emulatori e dei dispositivi reali su cui vengono eseguiti i test strumentati. Crea specifiche per 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 l'integrazione continua (CI), puoi assicurarti che i test appropriati vengano eseguiti quando necessario, eseguendo il provisioning e l'avvio degli emulatori e semplificando la configurazione della 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 più esempi di GMD nel progetto testing-samples.
Firebase Test Lab
Utilizza Firebase Test Lab (FTL) o un servizio di farm di dispositivi simile per eseguire i tuoi test su dispositivi reali specifici a cui potresti non avere accesso, come pieghevoli o tablet di varie dimensioni. Firebase Test Lab è un servizio a pagamento con un piano 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 di dispositivi ed emulatori in anticipo.
Per informazioni sull'utilizzo di FTL con GMD, consulta Scalare i test con i dispositivi gestiti da Gradle.
Filtrare i test con il test runner
Una strategia di test ottimale non deve verificare la stessa cosa due volte, quindi la maggior parte dei test dell'interfaccia utente non deve essere eseguita su più dispositivi. In genere, filtri i test dell'interfaccia utente eseguendo tutti o la maggior parte di essi su un fattore di forma dello smartphone e solo un sottoinsieme su dispositivi con dimensioni dello schermo diverse.
Puoi annotare determinati test da eseguire solo con determinati dispositivi e poi passare un argomento a AndroidJUnitRunner utilizzando il comando che esegue i test.
Ad esempio, puoi creare annotazioni diverse:
annotation class TestExpandedWidth
annotation class TestCompactWidth
E utilizzarli in test diversi:
class MyTestClass {
@Test
@TestExpandedWidth
fun myExample_worksOnTablet() {
...
}
@Test
@TestCompactWidth
fun myExample_worksOnPortraitPhone() {
...
}
}
Puoi quindi utilizzare la proprietà android.testInstrumentationRunnerArguments.annotation durante l'esecuzione dei 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 gli emulatori su CI, assicurati innanzitutto che l'emulatore o il dispositivo corretto sia pronto e connesso, quindi passa il parametro a uno dei comandi Gradle per eseguire i test strumentati:
$ ./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'
Tieni presente che Espresso Device (vedi la sezione successiva) può anche filtrare i test utilizzando le proprietà del dispositivo.
Espresso Device
Utilizza Espresso Device per eseguire azioni sugli emulatori nei test utilizzando qualsiasi tipo di test strumentato, inclusi i test Espresso, Compose o UI Automator. Queste azioni potrebbero 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. Espresso Device 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 Espresso Device è ancora in fase alfa e presenta i seguenti requisiti:
- Plug-in Android per Gradle versione 8.3 o successive
- Emulatore Android versione 33.1.10 o successive
- Dispositivo virtuale Android con livello API 24 o superiore
Filtrare i test
Espresso Device 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 RequiresDeviceMode
L'RequiresDeviceMode annotazione può essere utilizzata più volte per indicare
un test che verrà eseguito solo se tutti i valori 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 RequiresDisplay
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 seguendo le classi di dimensioni delle finestre ufficiali.
class OnDeviceTest {
...
@Test
@RequiresDisplay(EXPANDED, COMPACT)
fun myScreen_expandedWidthCompactHeight() {
...
}
}
Ridimensionare gli schermi
Utilizza il metodo setDisplaySize() per ridimensionare le dimensioni dello schermo
in fase di runtime. Utilizza il metodo insieme alla DisplaySizeRule
classe, che garantisce che tutte 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.
}
}
Quando ridimensioni uno schermo con setDisplaySize(), non influisci sulla densità
del dispositivo, quindi se una dimensione non rientra nel dispositivo di destinazione, il test
non riesce con un'UnsupportedDeviceOperationException. Per evitare che i test vengano eseguiti in questo caso, utilizza l'annotazione RequiresDisplay per filtrarli:
@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
La classe StateRestorationTester viene utilizzata per testare il ripristino dello stato
dei componenti componibili senza ricreare le attività. In questo modo i test sono 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 Window Testing
La libreria Window Testing contiene utilità che ti aiutano a scrivere test che si basano su o verificano funzionalità correlate alla gestione delle finestre, come l'incorporamento delle attività o le funzionalità pieghevoli. L'artefatto è disponibile tramite il repository Maven di Google.
Ad esempio, puoi utilizzare la FoldingFeature() funzione per generare un
personalizzato FoldingFeature, che puoi utilizzare nelle anteprime di Compose. In Java,
utilizza la createFoldingFeature() funzione.
In un'anteprima di Compose, puoi 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à dello schermo nei test dell'interfaccia utente utilizzando la
TestWindowLayoutInfo() funzione.
L'esempio seguente simula un FoldingFeature con una
HALF_OPENED
cerniera verticale al centro dello schermo, quindi verifica se il
layout è 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 WindowManager.
Risorse aggiuntive
Documentazione
- Norme sulla qualità delle app per schermi di grandi dimensioni
- Testare le app su Android
- Testare il layout di Compose
Esempi
- Esempio di WindowManager
- Esempi di Espresso Device
- Now in Android
- Utilizza i test degli screenshot per verificare le diverse dimensioni dello schermo
Codelab