Android Compose UI Test 測試與除錯
在宣告式 UI 中,我們不透過 viewId 來尋找元件,而是透過「語意 (Semantics)」系統。Compose 的測試 API 模擬了使用者的互動,能幫助你確保 UI 流程與輔助功能 (Accessibility) 的正確性。
語意樹 (Semantics Tree)
Compose 渲染 UI 時,同步會產生一棵「語意樹」。這棵樹描述了每個節點的用途(如:這是個按鈕、這段文字的內容是...)。
- Merged Tree (預設):為了無障礙工具(如 TalkBack),Compose 會將多個子節點合併。例如一個
Button包含一個Text,測試時會被視為一個可點擊的整體。 - Unmerged Tree:如果你需要測試按鈕內特定文字的細節,可以使用
useUnmergedTree = true展開它。
測試結構:AAA 模式
在 src/androidTest/java 下建立測試,並使用 createComposeRule()。
class LoginTest {
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun loginFlow_success() {
// 1. Arrange (準備環境)
composeTestRule.setContent { MyTheme { LoginScreen() } }
// 2. Act (執行動作)
composeTestRule.onNodeWithTag("username").performTextInput("admin")
composeTestRule.onNodeWithText("登入").performClick()
// 3. Assert (斷言狀態)
composeTestRule.onNodeWithText("歡迎回來").assertIsDisplayed()
}
}
API 工具手冊
Matchers (尋找節點)
onNodeWithText("文字"):最直觀的尋找方式。onNodeWithTag("MyTag"):當文字不固定時,使用Modifier.testTag()。onNodeWithContentDescription("描述"):基於無障礙說明的尋找方式(推薦)。hasParent(matcher):階層式過濾。
Actions (執行動作)
performClick():模擬點擊。performTextInput("..."):鍵入文字。performScrollTo():滾動到該節點出現在畫面中。performImeAction():觸發鍵盤動作(如:送出)。
Assertions (斷言結果)
assertIsDisplayed():確認節點在畫面上。assertIsEnabled()/assertIsNotEnabled():確認按鈕狀態。assertIsSelected():確認單選/複選框狀態。assertTextEquals("..."):字串精確比對。
測試非同步與等待
如果 UI 需要等待網路載入或動畫,可以使用 waitUntil 系列 API。
@Test
fun testAsyncLoading() {
composeTestRule.setContent { DataScreen() }
// 等待直到 "載入完成" 這四個字出現在語意樹中 (超時 5 秒)
composeTestRule.waitUntil(timeoutMillis = 5000) {
composeTestRule
.onAllNodesWithText("載入完成")
.fetchSemanticsNodes().isNotEmpty()
}
composeTestRule.onNodeWithText("載入完成").assertIsDisplayed()
}
除錯與分析工具
Layout Inspector
Android Studio 內建工具。它能讓你即時看到:
- 節點屬性:具體的寬高、間隔與參數。
- 重組統計 (Recomposition):哪些組件非預期地反覆繪製,幫助解決效能瓶頸。
印出語意樹 (Debugging)
如果在測試中找不到節點,可以將整棵語意樹印出來查看結構:
composeTestRule.onRoot().printToLog("MY_DEBUG_TAG")
遵循 使用者導向 的測試原則(多用文字與描述,少用 Tag),不僅能提高測試覆蓋率,還能確保你的 App 對於身障使用者同樣友善。