Android 提供多種工具和 API,可協助您針對不同螢幕大小和視窗大小建立測試。
DeviceConfigurationOverride
DeviceConfigurationOverride
可組合項可讓您覆寫設定屬性,測試 Compose 版面配置中的多種螢幕和視窗大小。ForcedSize
覆寫值適合可用空間中的任何版面配置,可讓您針對任何螢幕大小執行任何 UI 測試。舉例來說,您可以使用小型手機板型規格執行所有 UI 測試,包括大型手機、折疊式裝置和平板電腦的 UI 測試。
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
) {
MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
}
![](https://developer.android.google.cn/static/images/training/testing/different-screens/nia_DeviceConfigurationOverride.png?authuser=3&hl=zh-tw)
此外,您可以利用這個可組合元件,設定字型大小、主題,以及您可能會想在不同視窗大小上測試的其他屬性。
Robolectric
使用 Robolectric,在 JVM「本機」上執行 Compose 或以 View 為基礎的 UI 測試,無需裝置或模擬器。您可以設定 Robolectric 以使用特定的螢幕大小和其他實用屬性。
在以下從「Now in Android」提供的範例中,Robolectric 已設為模擬 1000x1000 dp 的解析度,且解析度為 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 { ... }
您也可以按照下列 Now in Android 範例中的程式碼片段操作,從測試主體中設定限定詞:
val (width, height, dpi) = ...
// Set qualifiers from specs.
RuntimeEnvironment.setQualifiers("w${width}dp-h${height}dp-${dpi}dpi")
請注意,RuntimeEnvironment.setQualifiers()
會使用新的設定更新系統和應用程式資源,但不會針對進行中活動或其他元件觸發任何動作。
詳情請參閱 Robolectric 裝置設定說明文件。
Gradle 管理的裝置
Gradle 管理的裝置 (GMD) Android Gradle 外掛程式可讓您定義模擬器和執行「檢測設備」測試的實際裝置規格。為不同螢幕大小的裝置建立規格以實作測試策略,其中必須在特定螢幕大小上執行特定測試。將 GMD 與持續整合 (CI) 搭配使用,即可確保在必要時執行適當的測試、佈建和啟動模擬器,以及簡化 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"
}
}
}
}
}
您可以在 testing-samples 專案中找到多個 GMD 範例。
Firebase Test Lab
請使用 Firebase Test Lab (FTL) 或類似的裝置農場服務,在您可能無法存取的特定實體裝置上執行測試,例如各種大小的折疊式裝置或平板電腦。Firebase Test Lab 是含免費方案的付費服務。FTL 也支援在模擬器上執行測試。這些服務可以預先佈建裝置和模擬器,因此能夠提高檢測測試的可靠性和速度。
如要瞭解如何搭配 GMD 使用 FTL,請參閱「使用 Gradle 管理的裝置擴充測試」。
使用測試執行工具測試篩選功能
最佳測試策略不應驗證兩次,因此大部分 UI 測試不需要在多部裝置上執行。一般而言,您會透過手機板型規格執行所有或大部分 UI 測試,並在具有不同螢幕尺寸的裝置中只執行一部分測試,以篩選 UI 測試。
您可以使用執行測試的指令,為特定裝置執行特定測試的註解,然後將引數傳送至 AndroidJUnitRunner。
舉例來說,您可以建立不同的註解:
annotation class TestExpandedWidth
annotation class TestCompactWidth
並用於不同的測試:
class MyTestClass {
@Test
@TestExpandedWidth
fun myExample_worksOnTablet() {
...
}
@Test
@TestCompactWidth
fun myExample_worksOnPortraitPhone() {
...
}
}
然後,您就能在執行測試時使用 android.testInstrumentationRunnerArguments.annotation
屬性來篩選特定測試。舉例來說,如果您使用的是 Gradle 管理的裝置:
$ ./gradlew pixelTabletApi30DebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'
如果您沒有使用 GMD,而且是透過 CI 管理模擬器,請先確認正確的模擬器或裝置已就緒並處於連線狀態,然後將該參數傳送至其中一個 Gradle 指令,執行檢測設備測試:
$ ./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'
請注意,Espresso Device (請參閱下一節) 也可以使用裝置屬性篩選測試。
Espresso 裝置
使用 Espresso Device 對使用任何類型的檢測設備測試 (包括 Espresso、Compose 或 UI Automator 測試),在測試中對模擬器執行動作。這些動作可能包括設定螢幕大小,或是切換摺疊式裝置狀態或型態。舉例來說,您可以控制折疊式模擬器,並將其設為桌面模式。Espresso 裝置也包含 JUnit 規則和註解,需要使用特定功能:
@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()))
}
}
請注意,Espresso Device 仍處於 Alpha 版階段,並符合下列條件:
- Android Gradle 外掛程式 8.3 以上版本
- Android Emulator 33.1.10 以上版本
- 搭載 API 級別 24 以上版本的 Android 虛擬裝置
篩選測試
Espresso 裝置可讀取已連結裝置的屬性,方便您使用註解篩選測試。如果不符合加註的要求,系統就會略過測試。
RequiresDeviceMode 註解
RequiresDeviceMode
註解可以多次使用,表示只有在裝置支援「所有」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()))
}
}
需要顯示註解
RequiresDisplay
註解可讓您使用大小類別指定裝置畫面的寬度和高度,這些類別會依據官方視窗大小類別定義維度值區。
class OnDeviceTest {
...
@Test
@RequiresDisplay(EXPANDED, COMPACT)
fun myScreen_expandedWidthCompactHeight() {
...
}
}
調整顯示畫面大小
在執行階段使用 setDisplaySize()
方法調整螢幕尺寸。搭配 DisplaySizeRule
類別使用方法,可確保在下次測試前復原測試期間所做的任何變更。
@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.
}
}
使用 setDisplaySize()
調整螢幕大小時,不會影響裝置的密度,因此如果尺寸與目標裝置不符,測試就會失敗,並顯示 UnsupportedDeviceOperationException
。如要避免在這種情況下執行測試,請使用 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.
}
}
StateRestorationTester
StateRestorationTester
類別可用來測試可組合元件的狀態還原作業,不必重新建立活動。活動重建是包含多個同步處理機制的複雜程序,因此上述做法可使測試更快、更可靠:
@Test
fun compactDevice_selectedEmailEmailRetained_afterConfigChange() {
val stateRestorationTester = StateRestorationTester(composeTestRule)
// Set content through the StateRestorationTester object.
stateRestorationTester.setContent {
MyApp()
}
// Simulate a config change.
stateRestorationTester.emulateSavedInstanceStateRestore()
}
視窗測試程式庫
視窗測試程式庫內含公用程式,可協助您撰寫需要或驗證與視窗管理相關的功能 (例如活動嵌入或折疊式功能) 的測試。您可透過 Google 的 Maven 存放區取得成果。
舉例來說,您可以使用 FoldingFeature()
函式產生自訂 FoldingFeature
,以便在 Compose 預覽中使用。在 Java 中,請使用 createFoldingFeature()
函式。
在 Compose 預覽中,您可以透過以下方式實作 FoldingFeature
:
@Preview(showBackground = true, widthDp = 480, heightDp = 480)
@Composable private fun FoldablePreview() =
MyApplicationTheme {
ExampleScreen(
displayFeatures = listOf(FoldingFeature(Rect(0, 240, 480, 240)))
)
}
此外,您也可以使用 TestWindowLayoutInfo()
函式,模擬 UI 測試中的顯示功能。以下範例以螢幕中心的 HALF_OPENED
垂直轉軸來模擬 FoldingFeature
,然後檢查該版面配置是否符合預期:
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()
}
}
觀看次數
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()))
}
}
您可以在 WindowManager 專案中找到更多範例。
其他資源
說明文件
範例
- WindowManager 範例
- Espresso 裝置範例
- Now In Android
- 使用螢幕截圖測試來驗證不同的螢幕大小
程式碼研究室