Android udostępnia różne narzędzia i interfejsy API, które mogą pomóc w tworzeniu testów na różnych ekranach i w oknach o różnych rozmiarach.
DeviceConfigurationOverride
Element kompozycyjny DeviceConfigurationOverride umożliwia zastępowanie atrybutów konfiguracji w celu testowania różnych rozmiarów ekranu i okna w układach Compose. Zastąpienie ForcedSize dopasowuje dowolny układ do dostępnego miejsca,
co pozwala na uruchamianie dowolnych
testów interfejsu na ekranach o dowolnym rozmiarze. Możesz na przykład użyć małego telefonu, aby uruchomić wszystkie testy interfejsu, w tym testy interfejsu 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.
}
Możesz też użyć tego elementu kompozycyjnego, aby 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 uruchamiać testy interfejsu Compose lub opartego na widokach lokalnie w JVM – nie są wymagane żadne urządzenia ani emulatory. Możesz skonfigurować Robolectric tak, aby używał określonych rozmiarów ekranu i innych przydatnych właściwości.
W tym przykładzie z aplikacji Now in Android Robolectric jest skonfigurowany tak, aby emulować ekran o rozmiarze 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 w treści testu, 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 o nową konfigurację, ale nie wywołuje żadnych działań w aktywnych aktywnościach ani innych komponentach.
Więcej informacji znajdziesz w dokumentacji konfiguracji urządzenia Robolectric Device Configuration.
Urządzenia zarządzane przez Gradle
Wtyczka Androida do obsługi Gradle urządzeń zarządzanych przez Gradle (GMD) umożliwia definiowanie specyfikacji emulatorów i rzeczywistych urządzeń, na których są uruchamiane testy instrumentowane. Utwórz specyfikacje urządzeń o różnych rozmiarach ekranu, aby wdrożyć strategię testowania, w której niektóre testy muszą być uruchamiane na ekranach o określonych rozmiarach. Używając GMD z trybem ciągłej integracji (CI), możesz mieć pewność, że w razie potrzeby zostaną uruchomione odpowiednie testy, a także że emulatory zostaną udostępnione i uruchomione, co 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
Użyj Laboratorium Firebase (FTL) lub podobnej usługi farmy urządzeń, aby uruchamiać swoje testy na określonych rzeczywistych urządzeniach, do których możesz nie mieć dostępu, takich jak urządzenia składane lub tablety o różnych rozmiarach. Laboratorium Firebase to usługa płatna z bezpłatnym pakietem. FTL obsługuje też uruchamianie testów na emulatorach. Te usługi zwiększają niezawodność i szybkość testów instrumentowanych, ponieważ mogą udostępniać urządzenia i emulatory z wyprzedzeniem.
Informacje o używaniu FTL z GMD znajdziesz w artykule Skalowanie testów za pomocą urządzeń zarządzanych przez Gradle.
Filtrowanie testów za pomocą narzędzia do uruchamiania testów
Optymalna strategia testowania nie powinna weryfikować tego samego dwa razy, dlatego większość testów interfejsu nie musi być uruchamiana na wielu urządzeniach. Zazwyczaj filtrujesz testy interfejsu, uruchamiając wszystkie lub większość z nich na telefonie, a tylko podzbiór na urządzeniach o różnych rozmiarach ekranu.
Możesz oznaczyć niektóre testy tak, aby były uruchamiane tylko na określonych urządzeniach, a następnie przekazać argument do AndroidJUnitRunner za pomocą polecenia, które uruchamia testy.
Możesz na przykład utworzyć 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() {
...
}
}
Podczas uruchamiania testów możesz użyć właściwości android.testInstrumentationRunnerArguments.annotation, aby filtrować określone testy. Jeśli na przykład 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 uruchomić testy instrumentowane:
$ ./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'
Pamiętaj, że Espresso Device (patrz następna sekcja) może też filtrować testy za pomocą właściwości urządzenia.
Espresso Device
Użyj Espresso Device, aby wykonywać działania na emulatorach w testach za pomocą dowolnego typu testów instrumentowanych, w tym testów Espresso, Compose lub UI Automator. Te działania mogą obejmować ustawianie rozmiaru ekranu lub przełączanie stanów i pozycji urządzenia składanego. Możesz na przykład sterować emulatorem urządzenia składanego i ustawić go w trybie stołowym. Espresso Device 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()))
}
}
Pamiętaj, że Espresso Device jest nadal w wersji alfa i ma te wymagania:
- Wtyczka Androida do obsługi Gradle w wersji 8.3 lub nowszej
- Android Emulator 33.1.10 lub nowszy
- Wirtualne urządzenie z Androidem z poziomem API 24 lub nowszym
Filtrowanie testów
Espresso Device może odczytywać właściwości połączonych urządzeń, aby umożliwić filtrowanie testów za pomocą adnotacji. Jeśli wymagania oznaczone adnotacjami nie są spełnione, testy są pomijane.
Adnotacja RequiresDeviceMode
Adnotacji RequiresDeviceMode można użyć wielokrotnie, aby wskazać
test, który będzie uruchamiany tylko wtedy, gdy urządzenie obsługuje 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()))
}
}
Adnotacja RequiresDisplay
Adnotacja RequiresDisplay umożliwia określenie szerokości i wysokości 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() {
...
}
}
Zmienianie rozmiaru ekranów
Użyj metody setDisplaySize(), aby zmienić rozmiar ekranu w czasie działania. Użyj tej metody w połączeniu z klasą DisplaySizeRule
, która zapewnia, że wszelkie zmiany wprowadzone podczas testów zostaną cofnięte przed następnym 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.
}
}
Gdy zmieniasz rozmiar ekranu za pomocą setDisplaySize(), nie wpływasz na gęstość
urządzenia, więc jeśli wymiar nie mieści się na urządzeniu docelowym, test
kończy się niepowodzeniem z powodu UnsupportedDeviceOperationException. Aby zapobiec uruchamianiu 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.
}
}
StateRestorationTester
Klasa StateRestorationTester służy do testowania przywracania stanu
komponentów kompozycyjnych bez ponownego tworzenia aktywności. Dzięki temu testy są szybsze i bardziej niezawodne, ponieważ ponowne tworzenie 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 korzystające z funkcji związanych z zarządzaniem oknami lub je weryfikujące, takich jak osadzanie aktywności czy funkcje urządzeń składanych. Artefakt jest dostępny w repozytorium Maven Google.
Możesz na przykład użyć funkcji FoldingFeature(), aby wygenerować niestandardową funkcję FoldingFeature, której możesz używać w podglądach Compose. W Javie,
użyj funkcji createFoldingFeature().
W podglądzie Compose możesz zaimplementować FoldingFeature w ten 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 ekranu w testach interfejsu za pomocą funkcji
TestWindowLayoutInfo().
Ten przykład symuluje FoldingFeature z
HALF_OPENED
pionowym zawiasem na ś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 WindowManager.
Dodatkowe materiały
Dokumentacja
- Wskazówki dotyczące jakości aplikacji na duże ekrany
- Testowanie aplikacji na Androidzie
- Testowanie układu Compose
Przykłady
- Przykład WindowManager
- Przykłady Espresso Device
- Now In Android
- Używa testowania zrzutów ekranu do weryfikowania różnych rozmiarów ekranu
Codelabs