Android udostępnia różne narzędzia i interfejsy API, które pomagają tworzyć testy dla różnych rozmiarów ekranów i okien.
DeviceConfigurationOverride
Funkcja kompozycyjna 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 przeprowadzić dowolny test interfejsu na ekranie o dowolnym rozmiarze. Możesz na przykład użyć małego telefonu, aby przeprowadzić 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.
}
Dodatkowo możesz użyć tego elementu kompozycyjnego, aby ustawić skalę czcionki, motywy i inne właściwości, które chcesz przetestować w różnych rozmiarach okna.
Robolectric
Używaj Robolectric do przeprowadzania testów interfejsu opartych na Compose lub widokach w JVM lokalnie – 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 Now in Android Robolectric jest skonfigurowany do emulowania ekranu 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 z 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 systemowe i zasoby aplikacji przy użyciu nowej konfiguracji, ale nie wywołuje żadnych działań w aktywnych aktywnościach ani innych komponentach.
Więcej informacji znajdziesz w dokumentacji Robolectric dotyczącej konfiguracji urządzenia.
Urządzenia zarządzane przez Gradle
Wtyczka Androida do obsługi Gradle urządzenia zarządzane przez Gradle (GMD) umożliwia określenie specyfikacji emulatorów i prawdziwych urządzeń, na których są uruchamiane testy z instrumentacją. Utwórz specyfikacje dla urządzeń o różnych rozmiarach ekranu, 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 trybem ciągłej integracji (CI), możesz mieć pewność, że odpowiednie testy będą uruchamiane w razie potrzeby, co pozwoli na 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"
}
}
}
}
}
Wiele przykładów GMD znajdziesz w projekcie testing-samples.
Laboratorium Firebase
Użyj Laboratorium Firebase lub podobnej usługi farmy urządzeń, aby przeprowadzić testy na konkretnych urządzeniach fizycznych, 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 poziomem bezpłatnym. FTL obsługuje też przeprowadzanie testów na emulatorach. Te usługi zwiększają niezawodność i szybkość testów z instrumentacją, ponieważ mogą wcześniej udostępniać urządzenia i emulatory.
Informacje o używaniu FTL z urządzeniami zarządzanymi przez Gradle znajdziesz w artykule Skalowanie testów za pomocą urządzeń zarządzanych przez Gradle.
Testowanie filtrowania za pomocą modułu uruchamiającego testy
Optymalna strategia testowania nie powinna sprawdzać tej samej rzeczy dwukrotnie, więc większość testów interfejsu nie musi być uruchamiana na wielu urządzeniach. Zwykle testy interfejsu są filtrowane przez uruchamianie wszystkich lub większości z nich na telefonie, a tylko podzbioru na urządzeniach z ekranami o różnych rozmiarach.
Możesz dodać adnotacje do niektórych testów, 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() {
...
}
}
Następnie możesz użyć właściwości android.testInstrumentationRunnerArguments.annotation podczas przeprowadzania testów, aby odfiltrować konkretne 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 z instrumentacją:
$ ./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.
Urządzenie Espresso
Używaj Espresso Device do wykonywania działań na emulatorach w testach z użyciem dowolnego typu testów z instrumentacją, w tym testów Espresso, Compose lub UI Automator. Może to być na przykład ustawienie rozmiaru ekranu lub przełączanie stanów albo pozycji urządzenia składanego. Możesz na przykład sterować emulatorem urządzenia składanego i ustawić go w trybie stołu. 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 fazie alfa i ma te wymagania:
- Wtyczka Androida do obsługi Gradle w wersji 8.3 lub nowszej
- Android Emulator w wersji 33.1.10 lub nowszej,
- urządzenie wirtualne z Androidem, które obsługuje interfejs API na poziomie 24 lub wyższym;
Filtruj testy
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 adnotacji nie są spełnione, testy są pomijane.
Adnotacja RequiresDeviceMode
Adnotacji RequiresDeviceMode można używać wielokrotnie, aby wskazać test, który zostanie uruchomiony tylko wtedy, gdy wszystkie wartości DeviceMode są obsługiwane 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()))
}
}
Adnotacja RequiresDisplay
Adnotacja RequiresDisplay pozwala określić szerokość i wysokość ekranu urządzenia za pomocą klas rozmiaru, które definiują grupy wymiarów zgodnie z oficjalnymi klasami rozmiaru okna.
class OnDeviceTest {
...
@Test
@RequiresDisplay(EXPANDED, COMPACT)
fun myScreen_expandedWidthCompactHeight() {
...
}
}
Zmienianie rozmiaru wyświetlaczy
Użyj metody setDisplaySize(), aby zmienić wymiary ekranu w czasie działania. Użyj tej metody w połączeniu z klasą DisplaySizeRule, która dba o to, aby wszystkie zmiany wprowadzone podczas testów zostały 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.
}
}
Gdy zmieniasz rozmiar wyświetlacza za pomocą setDisplaySize(), nie wpływa to na gęstość urządzenia, więc jeśli wymiar nie pasuje do urządzenia docelowego, test kończy się niepowodzeniem i wyświetla się błąd UnsupportedDeviceOperationException. Aby zapobiec uruchamianiu testów w takim 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ż 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 Window Testing
Biblioteka Window Testing zawiera narzędzia, które pomagają pisać testy oparte na funkcjach związanych z zarządzaniem oknami, takich jak osadzanie aktywności czy funkcje urządzeń składanych, lub weryfikujące te funkcje. Artefakt jest dostępny w repozytorium Maven Google.
Możesz na przykład użyć funkcji FoldingFeature(), aby wygenerować niestandardowy obiekt FoldingFeature, którego możesz używać w podglądach Compose. W języku Java 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 wyświetlania w testach interfejsu za pomocą funkcji TestWindowLayoutInfo().
Poniższy przykład symuluje urządzenie FoldingFeature z pionowym zawiasem HALF_OPENED 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ładowy kod WindowManager
- Przykłady Espresso Device
- Now in Android
- wykorzystuje testowanie zrzutów ekranu do weryfikacji różnych rozmiarów ekranu;
Codelabs