Android udostępnia wiele narzędzi i interfejsów API, które pomagają tworzyć testy na ekrany i okna o różnych rozmiarach.
DeviceConfigurationOverride
Element kompozycyjny DeviceConfigurationOverride
pozwala zastąpić atrybuty konfiguracji w celu przetestowania różnych rozmiarów ekranu i okien w układach tworzenia wiadomości. Zastępowanie elementu ForcedSize
pasuje do każdego układu w dostępnym miejscu, dzięki czemu możesz przeprowadzić dowolny test interfejsu na ekranie o dowolnym rozmiarze. Możesz na przykład przeprowadzić wszystkie testy UI, w tym testy UI na dużych telefonach, urządzeniach składanych i tabletach.
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
) {
MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
}
Dodatkowo za pomocą tego elementu kompozycyjnego możesz ustawić skalę czcionki, motywy i inne właściwości, które chcesz przetestować w oknach o różnych rozmiarach.
Robolectric
Użyj Robolectric, aby przeprowadzać testy interfejsu Compose lub oparte na wyświetleniu w JVM lokalnie – nie są wymagane urządzenia ani emulatory. Możesz skonfigurować program Roobolectric, aby używać określonych rozmiarów ekranu i innych przydatnych właściwości.
W tym przykładzie z Now in Android Robolectric skonfigurowano do emulacji ekranu o rozdzielczości 1000 x 1000 dp i 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 testu, tak jak w tym fragmencie kodu z przykładu Now in Android:
val (width, height, dpi) = ...
// Set qualifiers from specs.
RuntimeEnvironment.setQualifiers("w${width}dp-h${height}dp-${dpi}dpi")
Pamiętaj, że RuntimeEnvironment.setQualifiers()
aktualizuje zasoby systemu i aplikacji przy użyciu nowej konfiguracji, ale nie wywołuje żadnych działań na aktywnych działaniach ani innych komponentach.
Więcej informacji znajdziesz w dokumentacji konfiguracji urządzenia Robolectric.
Urządzenia zarządzane przez Gradle
Wtyczka Androida do obsługi urządzeń zarządzanych przez Gradle (GMD) pozwala określić specyfikacje emulatorów i prawdziwych urządzeń, na których uruchamiane są instrumenty testów. Utwórz specyfikacje dla urządzeń z ekranami o różnych rozmiarach, aby wdrożyć strategię testowania, w ramach której określone testy muszą być przeprowadzane na ekranach o określonych rozmiarach. Używając GMD z Ciągłą integracją (CI), możesz mieć pewność, że w razie potrzeby uruchamiają się odpowiednie testy, udostępniać i uruchamiać emulatory oraz uprościć konfigurację 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"
}
}
}
}
}
Wiele przykładów GMD znajdziesz w projekcie testing-samples.
Laboratorium Firebase
Skorzystaj z Laboratorium Firebase lub podobnej usługi farmy urządzeń, by przeprowadzić testy na konkretnych rzeczywistych urządzeniach, do których możesz nie mieć dostępu, takich jak urządzenia składane lub tablety o różnej wielkości. Laboratorium Firebase to płatna usługa na poziomie bezpłatnym. FTL obsługuje też testy w emulatorach. Usługi te zwiększają niezawodność i szybkość testów z instrumentacją, ponieważ mogą udostępnić urządzenia i emulatory z wyprzedzeniem.
Informacje o korzystaniu z FTL w GMD znajdziesz w artykule Skalowanie testów przy użyciu urządzeń zarządzanych przez Gradle.
Filtrowanie testów za pomocą komponentu uruchamiającego test
Optymalna strategia testowa nie powinna weryfikować tego samego elementu dwa razy, więc większość testów interfejsu nie musi być przeprowadzana na wielu urządzeniach. Zwykle filtrujesz testy interfejsu, wykonując wszystkie lub większość z nich na telefonie i tylko na niektórych urządzeniach z ekranami o różnych rozmiarach.
Możesz dodać adnotacje do wybranych testów, które mają zostać przeprowadzone tylko na określonych urządzeniach, a następnie przekazać argument do AndroidJUnitRunner, używając polecenia, które uruchamia testy.
Możesz na przykład tworzyć różne adnotacje:
annotation class TestExpandedWidth
annotation class TestCompactWidth
i używać ich w różnych testach:
class MyTestClass {
@Test
@TestExpandedWidth
fun myExample_worksOnTablet() {
...
}
@Test
@TestCompactWidth
fun myExample_worksOnPortraitPhone() {
...
}
}
Następnie możesz używać właściwości android.testInstrumentationRunnerArguments.annotation
do filtrowania konkretnych testów podczas wykonywania testów. Na przykład jeśli używasz urządzeń 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 gotowe i połączone, a następnie przekaż parametr do jednego z poleceń Gradle, aby przeprowadzić 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 na podstawie właściwości urządzenia.
Ekspres do kawy
Urządzenie Espresso pozwala wykonywać działania na emulatorach w testach z wykorzystaniem dowolnego typu testów z instrumentami, np. Espresso, Compose czy Automator interfejsu. Czynności te mogą obejmować ustawianie rozmiaru ekranu oraz przełączanie trybów składanych i pozycji. Możesz na przykład sterować składanym emulatorem i ustawić go w trybie stołu. Zawiera też reguły i adnotacje JUnit, które wymagają określonych 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()))
}
}
Uwaga: urządzenie Espresso jest nadal w wersji alfa i ma następujące wymagania:
- Wtyczka Androida do obsługi Gradle w wersji 8.3 lub nowszej
- Emulator Androida w wersji 33.1.10 lub nowszej
- Wirtualne urządzenie z Androidem z interfejsem API na poziomie 24 lub wyższym
Filtruj testy
Urządzenie do espresso może odczytywać właściwości połączonych urządzeń, aby umożliwić Ci filtrowanie testów za pomocą adnotacji. Jeśli wymagania z adnotacjami nie zostaną spełnione, testy są pomijane.
Adnotacja RequiresDeviceMode
Adnotacji RequiresDeviceMode
można używać wielokrotnie, aby wskazać test, który zostanie uruchomiony tylko wtedy, gdy urządzenie będzie obsługiwać wszystkie wartości DeviceMode
.
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 Display
Adnotacja RequiresDisplay
pozwala określić szerokość i wysokość ekranu urządzenia za pomocą klas rozmiarów, które definiują zasobniki wymiarów zgodnie z oficjalnymi klasami rozmiarów okien.
class OnDeviceTest {
...
@Test
@RequiresDisplay(EXPANDED, COMPACT)
fun myScreen_expandedWidthCompactHeight() {
...
}
}
Zmiana rozmiaru wyświetlaczy
Aby zmieniać rozmiar ekranu w czasie działania, użyj metody setDisplaySize()
. Używaj jej w połączeniu z klasą DisplaySizeRule
, dzięki czemu wszelkie zmiany wprowadzone podczas testów zostaną cofnięte przed kolejnym testem.
@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.
}
}
Zmiana rozmiaru wyświetlacza za pomocą funkcji setDisplaySize()
nie ma wpływu na gęstość urządzenia. Jeśli więc wymiar nie pasuje do urządzenia docelowego, test kończy się wynikiem UnsupportedDeviceOperationException
. Aby w takim przypadku uniemożliwić uruchomienie testów, odfiltruj je za pomocą adnotacji RequiresDisplay
:
@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.
}
}
Stanowy tester rewitalizacji
Klasa StateRestorationTester
służy do testowania przywracania stanu komponentów kompozycyjnych bez ponownego tworzenia działań. Dzięki temu testy są szybsze i bardziej niezawodne, ponieważ odtwarzanie aktywności to złożony proces z wieloma mechanizmami 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 testowania okien
Biblioteka testowania okien zawiera narzędzia, które pomagają pisać testy, które bazują na funkcjach związanych z zarządzaniem oknami lub je sprawdzają, takie jak umieszczanie aktywności czy funkcje składane. Artefakt jest dostępny w repozytorium Google Manufacturer Center.
Możesz na przykład użyć funkcji FoldingFeature()
, aby wygenerować niestandardowy FoldingFeature
, którego możesz 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)))
)
}
Możesz też emulować funkcje wyświetlania w testach interfejsu za pomocą funkcji TestWindowLayoutInfo()
.
Ten przykład pokazuje symulację elementu FoldingFeature
z pionowym zawiasem HALF_OPENED
pośrodku ekranu, a następnie sprawdza, czy układ jest zgodny z oczekiwanym:
Utwórz
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 WindowManagera.
Dodatkowe materiały
Dokumentacja
- Wskazówki dotyczące jakości aplikacji na duży ekran
- Testowanie aplikacji na Androidzie
- Testowanie układu tworzenia wiadomości
Próbki
- Przykład usługi WindowManager
- Próbki urządzenia do espresso
- Teraz na Androidzie
- Wykorzystuje testowanie zrzutów ekranu w celu weryfikacji różnych rozmiarów ekranu
Ćwiczenia z programowania