Biblioteki i narzędzia do testowania różnych rozmiarów ekranów

Android udostępnia wiele narzędzi i interfejsów API, które mogą pomóc w tworzeniu testów różne rozmiary ekranów i okna.

Zastąpienie konfiguracji urządzenia

Funkcja kompozycyjna DeviceConfigurationOverride umożliwia zastąpienie atrybutów konfiguracji do testowania różnych rozmiarów ekranów i okien w Compose układy. Zastąpienie ForcedSize pasuje do dowolnego układu w dostępnym miejscu. który pozwala uruchomić Test interfejsu na dowolnym ekranie Na przykład możesz użyć małego telefonu do uruchamiania wszystkich testów UI, w tym testów UI na duże telefony, urządzenia składane na tabletach.

   DeviceConfigurationOverride(
        DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
    ) {
        MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
    }
.
Rysunek 1. Użycie elementu DeviceConfigurationOverride w celu dopasowania układu tabletu do mniejszych urządzeń, na przykład \*Now w Androidzie*.

Za pomocą tej funkcji możesz też ustawić skalę czcionki, motywy które warto przetestować w oknach o różnych rozmiarach.

Robolectric

Użyj Robolectric, aby uruchomić w środowisku JVM narzędzie Compose lub testy interfejsu oparte na wyświetlaniu lokalnie – nie są wymagane żadne urządzenia ani emulatory; Możesz skonfigurować Robolectric, aby korzystać z określonych rozmiarów ekranów i innych przydatnych właściwości.

W tym przykładzie z Teraz na Androidzie rozwiązanie Robolectric zostało skonfigurowane. emulację rozmiaru ekranu 1000 x 1000 dp z rozdzielczością 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 { ... }

Możesz też ustawić kwalifikatory z treści testowej tak, jak w tym fragmencie kodu przykład Now na Androidzie:

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

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

Pamiętaj, że RuntimeEnvironment.setQualifiers() aktualizuje system i zasobów aplikacji z nową konfiguracją, ale nie aktywuje żadnego działania na aktywne aktywności lub inne elementy.

Więcej informacji znajdziesz w dokumentacji konfiguracji urządzenia Robolectric.

Urządzenia zarządzane przez Gradle

Wtyczka Androida do obsługi Gradle dla urządzeń zarządzanych przez Gradle (GMD) pozwala zdefiniować specyfikacje emulatorów i prawdziwych urządzeń, testów instrumentowanych. Utwórz specyfikacje dla urządzeń z różne rozmiary ekranów, aby wdrożyć strategię testowania, w ramach której na ekranach o określonych rozmiarach. za pomocą GMD z ciągłą integracją (CI), możesz zadbać o to, aby w razie potrzeby uruchomić odpowiednie testy udostępnianie i uruchamianie emulatorów oraz uproszczenie konfiguracji 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"
                }
            }
        }
    }
}

Kilka przykładów GMD znajdziesz w projekcie testing-samples.

Laboratorium Firebase

Skorzystaj z Laboratorium Firebase (FTL) lub podobnej usługi farmy urządzeń, by przeprowadzić na konkretnych urządzeniach, do których możesz nie mieć dostępu, takich jak na urządzeniach składanych lub tabletach o różnych rozmiarach; Laboratorium Firebase to płatna na poziomie bezpłatnym. FTL umożliwia też przeprowadzanie testów na emulatorach. Usługi te zwiększają niezawodność i szybkość testów instrumentalnych, ponieważ mogą wcześniej zapewnić obsługę urządzeń i emulatorów.

Informacje o korzystaniu z FTL w połączeniu z GMD znajdziesz w sekcji Skalowanie testów z użyciem GMD Urządzenia zarządzane przez Gradle

Testowanie filtrowania za pomocą mechanizmu uruchamiania testów

Optymalna strategia testowa nie powinna sprawdzać tego samego elementu dwukrotnie, dlatego większość Testy interfejsu nie muszą być przeprowadzane na różnych urządzeniach. Zwykle filtrujemy UI wszystkie lub większość z nich przeprowadza się na telefonach komórkowych, a tylko w niektórych przypadkach. urządzeń o różnych rozmiarach ekranów.

Możesz dodawać adnotacje do testów, które mają być uruchamiane tylko na określonych urządzeniach, a potem zakończone argument w AndroidJUnitRunner za pomocą polecenia, które uruchamia testów.

Możesz na przykład utworzyć różne adnotacje:

annotation class TestExpandedWidth
annotation class TestCompactWidth

Użyj ich w różnych testach:

class MyTestClass {

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

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

}

Następnie możesz użyć usługi android.testInstrumentationRunnerArguments.annotation do filtrowania określonych elementów. Jeśli na przykład jesteś na urządzeniach zarządzanych przez Gradle:

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

Jeśli nie używasz GMD i zarządzasz emulatorami w CI, najpierw upewnij się, że odpowiedni emulator lub urządzenie jest gotowy i podłączony, a następnie przekazuje parametr za pomocą jednego z poleceń Gradle, które pozwalają uruchomić testy instrumentowane:

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

Pamiętaj, że urządzenie do espresso (patrz następna sekcja) może też filtrować testy, stosując właściwości urządzenia.

Ekspres do kawy

Za pomocą Espresso Device możesz wykonywać działania na emulatorach w testach, używając dowolnego typu testów instrumentalnych, w tym testów Espresso, Compose i UI Automator. Mogą one obejmować ustawianie rozmiaru ekranu lub przełączanie stanów urządzenia składanego. ani różnych stanów. Możesz na przykład sterować składanym emulatorem i ustawiać go na trybu stołu. Urządzenie do espresso zawiera też reguły JUnit i adnotacje do wymagają pewnych funkcji:

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

Pamiętaj, że urządzenie Espresso jest wciąż w fazie alfa i oferuje następujące funkcje: wymagania:

  • Wtyczka Androida do obsługi Gradle w wersji 8.3 lub nowszej
  • Emulator Androida w wersji 33.1.10 lub nowszej
  • Urządzenie wirtualne z Androidem, na którym działa interfejs API na poziomie 24 lub wyższym

Filtruj testy

Urządzenie do espresso może odczytywać właściwości połączonych urządzeń, by umożliwić Ci filtrowanie testów z użyciem adnotacji. Jeśli wymagania z adnotacjami nie są spełnione, są pomijane.

Wymaga adnotacji trybu urządzenia

Adnotacji RequiresDeviceMode można używać wiele razy, aby wskazać, test, który będzie przeprowadzany tylko wtedy, gdy obsługiwane będą wszystkie wartości DeviceMode na urządzeniu.

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

Wymaga adnotacji displayowej

Adnotacja RequiresDisplay umożliwia określenie szerokości i wysokości ekranu urządzenia za pomocą klas rozmiaru, które definiują segmenty wymiarów. zgodnie z oficjalnymi klasami dotyczącymi rozmiaru okna.

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

Zmień rozmiar wyświetlaczy

Użyj metody setDisplaySize(), aby zmienić wymiary ekranu w czasie działania aplikacji. Używaj tej metody w połączeniu z DisplaySizeRule dzięki czemu wszystkie zmiany wprowadzone podczas testów są cofnięte przed następny test.

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

Gdy zmieniasz rozmiar wyświetlacza za pomocą funkcji setDisplaySize(), nie wpływa to na gęstość na urządzeniu. Jeśli więc wymiar nie pasuje do urządzenia docelowego, test jest przeprowadzany kończy się niepowodzeniem na UnsupportedDeviceOperationException. Aby uniemożliwić przeprowadzanie testów w tym przypadku, użyj adnotacji RequiresDisplay, aby je odfiltrować:

@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 przywrócenia stanu

Klasa StateRestorationTester jest używana do testowania przywracania stanu dla komponentów kompozycyjnych bez odtwarzania działań. To przyspiesza przeprowadzanie testów i bardziej niezawodne, ponieważ rekreacja to złożony proces mechanizmy synchronizacji:

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

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

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

Biblioteka testów okien

Biblioteka Window Testing zawiera narzędzia pomocne w pisaniu testów włączanie i weryfikowanie funkcji związanych z zarządzaniem oknami, takich jak aktywność w dodatku i funkcjach składanych. Artefakt jest dostępny w naszej witrynie Repozytorium Maven.

Możesz na przykład użyć funkcji FoldingFeature() do wygenerowania niestandardowy FoldingFeature, którego można używać w podglądach tworzenia wiadomości. W Javie użyj funkcji createFoldingFeature().

W podglądzie tworzenia wiadomości możesz zaimplementować FoldingFeature w taki sposób:

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

W testach interfejsu możesz też emulować funkcje displayowe za pomocą interfejsu TestWindowLayoutInfo(). W poniższym przykładzie symulowane jest FoldingFeature z HALF_OPENED. pionowy zawias na środku ekranu, a następnie sprawdza, czy zgodnie z oczekiwaniami:

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

Wyświetlenia

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

Więcej przykładów znajdziesz w projekcie WindowManager.

Dodatkowe materiały

Dokumentacja

Próbki

Ćwiczenia z programowania