互通性

Compose 可與常見的測試架構整合。

與 Espresso 的互通性

在混合式應用程式中,您可以在檢視區塊階層中找到 Compose 元件,並在 Compose 可組合函式中找到檢視區塊 (透過 AndroidView 可組合函式)。

這兩種類型不需要任何特殊步驟。您可以透過 Espresso 的 onView 比對檢視畫面,並透過 ComposeTestRule 比對 Compose 元素。

@Test
fun androidViewInteropTest() {
    // Check the initial state of a TextView that depends on a Compose state.
    Espresso.onView(withText("Hello Views")).check(matches(isDisplayed()))
    // Click on the Compose button that changes the state.
    composeTestRule.onNodeWithText("Click here").performClick()
    // Check the new value.
    Espresso.onView(withText("Hello Compose")).check(matches(isDisplayed()))
}

新增 View 範圍語意,用於 Compose 互通性測試

將 Compose 搜尋範圍限定在特定 View

將複雜的 UI 遷移至 Compose 時,您可能會遇到多個傳統 Android View (例如 RecyclerView 或 ViewPager) 內嵌相同的 Compose 元素。在這些情況下,標準 Compose 搜尋 (例如 onNodeWithText("Save")) 可能會失敗,並顯示「找到多個節點」錯誤。

您可以直接將 Compose 測試範圍限定在特定 Android 檢視畫面,而不必修改正式版程式碼,插入動態測試標記來區分這些元素。

對測試規則使用 onRootWithViewInteraction API。這個函式接受 Espresso ViewInteraction,可讓您利用 Espresso 隔離特定容器 View,並僅在該範圍階層中執行 Compose 互動。

與清單項目互動

如要與特定 RecyclerView 列中的 Compose 元素互動,請使用 Espresso 找出該列,然後將 Compose 互動範圍限定在該列。這會忽略所有其他資料列中的相同 Compose 元素。

@Test
fun testComposeButtonInsideRecyclerViewItem() = runComposeUiTest {
    // Scroll to the desired position using Espresso
    Espresso.onView(withId(recyclerViewId))
        .perform(RecyclerViewActions.scrollToPosition<MyViewHolder>(3))

    // Define an Espresso ViewInteraction that uniquely identifies the row
    val rowView = Espresso.onView(
        allOf(
            withId(rootViewId),
            hasDescendant(withText("Item #3"))
        )
    )

    // Scope the Compose search strictly to that specific row View
    onRootWithViewInteraction(rowView)
        .onNode(hasText("Like"))
        .performClick()
}

解決 ViewPager 中的模稜兩可問題

當記憶體中同時有多個片段具有相同的 Compose 版面配置時,您可以將搜尋範圍限定在特定片段的根檢視畫面 ID,避免比對結果模糊不清。

@Test
fun testComposeButtonInsideViewPagerItem() = runComposeUiTest {
    // Swipe to the desired page using Espresso
    Espresso.onView(withId(viewPagerViewId)).perform(swipeLeft())

    // Identify the specific container view using Espresso
    val fragmentB = Espresso.onView(withId(fragmentRootViewId))

    // The generic text "Save" is now unique within this view scope
    onRootWithViewInteraction(fragmentB)
        .onNode(hasText("Save"))
        .assertIsDisplayed()
}

與 UiAutomator 的互通性

根據預設,可組合項只能透過便利描述項 (顯示文字、內容說明等) 從 UiAutomator 存取。如需存取任何使用 Modifier.testTag 的可組合項,則需為特定的可組合項子樹狀結構啟用語意屬性 testTagsAsResourceId。如果可組合項並無其他任何專屬控制代碼,例如捲動式可組合項 (例如:LazyColumn),啟用這項行為會非常實用。

請在可組合項階層的高層級啟用語意屬性,確保所有設有 Modifier.testTag 的巢狀結構可組合項都可以從 UiAutomator 存取。

Scaffold(
    // Enables for all composables in the hierarchy.
    modifier = Modifier.semantics {
        testTagsAsResourceId = true
    }
){
    // Modifier.testTag is accessible from UiAutomator for composables nested here.
    LazyColumn(
        modifier = Modifier.testTag("myLazyColumn")
    ){
        // Content
    }
}

任何設有 Modifier.testTag(tag) 的可組合項都可以藉由使用 By.res(resourceName) 並利用相同的 tag 做為 resourceName 的方式存取。

val device = UiDevice.getInstance(getInstrumentation())

val lazyColumn: UiObject2 = device.findObject(By.res("myLazyColumn"))
// Some interaction with the lazyColumn.

其他資源

  • 在 Android 上測試應用程式:Android 測試的主要到達網頁,提供更廣泛的測試基礎知識和技術。
  • 測試基礎知識:進一步瞭解測試 Android 應用程式的核心概念。
  • 本機測試:您可以在自己的工作站本機執行部分測試。
  • 檢測設備測試:建議您也執行檢測設備測試。也就是直接在裝置端執行的測試。
  • 持續整合:持續整合可讓您將測試整合至部署管道。
  • 測試不同的螢幕大小:使用者可用的裝置有很多,因此您應測試不同的螢幕大小。
  • Espresso:雖然 Espresso 適用於以 View 為基礎的 UI,但 Espresso 知識仍有助於 Compose 測試的某些方面。