Espresso Web

Espresso-Web 是用來處理 Android WebView UI 元件的進入點。 Espresso-Web 會重複使用熱門 WebDriver API 中的 Atom,檢查及控管 WebView 的行為。

Espresso-Web 的適用時機

使用 Espresso-Web 測試混合型應用程式,尤其是應用程式原生 UI 元件與 WebView UI 元件的整合。您可以搭配使用 Espresso-Web API 與其他 Espresso API,與 WebView 物件內的網頁元素充分互動。

如果您只需要測試 WebView 本身,而非應用程式中 WebView 與原生元件之間的互動,請考慮使用 WebDriver 等架構編寫一般網頁測試。如果您使用網路測試架構,就不需要使用 Android 裝置或 Java 虛擬機器,因此測試執行速度更快,也更可靠。不過,Espresso-Web 可讓您重複使用自訂 WebDriver 原子,因此具有高度彈性,特別是在編寫要針對獨立網頁應用程式和包含 Android UI 的應用程式執行的測試時。

運作方式

與 Espresso 的 onData() 方法類似,WebView 互動包含多個 Atom。WebView 互動會結合 Java 程式設計語言和 JavaScript 橋接器來執行工作。由於從 JavaScript 環境公開資料不會造成競爭條件,因此完全支援從 Web.WebInteraction 物件傳回資料,讓您驗證從要求傳回的所有資料。這是因為 Espresso 在 Java 端看到的所有內容都是獨立副本。

什麼是 WebDriver Atom?

WebDriver 架構會使用 Atom,以程式輔助方式尋找及操控網頁元素。WebDriver 會使用 Atom 來操控瀏覽器。從概念上來說,Atom 與 ViewAction 類似,都是獨立單元,可在 UI 中執行動作。您可以使用定義的方法清單 (例如 findElement()getElement()) 公開 Atom,從使用者的角度驅動瀏覽器。不過,如果您直接使用 WebDriver 架構,就必須妥善安排 Atoms,這需要相當冗長的邏輯。

在 Espresso 中,WebWeb.WebInteraction 類別會包裝這個樣板,並提供與 WebView 物件互動的 Espresso 類似體驗。因此在 WebView 的環境中,Atoms 可用來取代傳統的 Espresso ViewMatchersViewActions

這個 API 隨後會變得相當簡單:

Kotlin

onWebView()
    .withElement(Atom)
    .perform(Atom)
    .check(WebAssertion)

Java

onWebView()
    .withElement(Atom)
    .perform(Atom)
    .check(WebAssertion);

詳情請參閱 Selenium 的 Atoms 說明文件

實作 WebView

請按照下列各節的操作說明,在應用程式測試中使用 WebView

套件

如要在專案中加入 Espresso-Web,請完成下列步驟:

  1. 開啟應用程式的 build.gradle 檔案。這通常不是頂層 build.gradle 檔案,而是 app/build.gradle
  2. 在依附元件中新增下列程式碼:

    Groovy

        androidTestImplementation 'androidx.test.espresso:espresso-web:3.6.1'
        

    Kotlin

        androidTestImplementation('androidx.test.espresso:espresso-web:3.6.1')
        
  3. Espresso-Web 僅與 Espresso 2.2 以上版本和測試程式庫 0.3 以上版本相容,因此請務必一併更新這些程式碼行:

    Groovy

        androidTestImplementation 'androidx.test:runner:1.6.1'
        androidTestImplementation 'androidx.test:rules:1.6.1'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
        

    Kotlin

        androidTestImplementation('androidx.test:runner:1.6.1')
        androidTestImplementation('androidx.test:rules:1.6.1')
        androidTestImplementation('androidx.test.espresso:espresso-core:3.6.1')
        

常見的 API 用法

使用 Espresso 在 Android 上處理 WebView 時,onWebView() 方法是主要進入點。您可以使用這個方法執行 Espresso-Web 測試,例如:

Kotlin

onWebView()
    .withElement(findElement(Locator.ID, "link_2")) // similar to onView(withId(...))
    .perform(webClick()) // Similar to perform(click())

    // Similar to check(matches(...))
    .check(webMatches(getCurrentUrl(), containsString("navigation_2.html")))

Java

onWebView()
    .withElement(findElement(Locator.ID, "link_2")) // similar to onView(withId(...))
    .perform(webClick()) // Similar to perform(click())

    // Similar to check(matches(...))
    .check(webMatches(getCurrentUrl(), containsString("navigation_2.html")));

在本例中,Espresso-Web 會找出 ID 為 "link_2" 的 DOM 元素,然後點選該元素。然後,這項工具會驗證 WebView 是否傳送包含 "navigation_2.html" 字串的 GET 要求。

JavaScript 支援

執行測試時,系統會使用 JavaScript 執行所有 WebView 互動。因此,如要支援 JavaScript 評估,測試中的 WebView 必須啟用 JavaScript。

如要強制啟用 JavaScript,請在受測活動中呼叫 forceJavascriptEnabled() 做為 action,如以下程式碼片段所示。

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @get:Rule val activityScenarioRule =
        activityScenarioRule<MyWebViewActivity>()

    @Test fun testWebViewInteraction() {
        onWebView().forceJavascriptEnabled()
    }
}

常見的網站互動

Web.WebInteraction 物件的常見互動包括:

  • withElement() 會參照 WebView 中的 DOM 元素。

    例子:

    Kotlin

    onWebView().withElement(findElement(Locator.ID, "teacher"))

    Java

    onWebView().withElement(findElement(Locator.ID, "teacher"));
  • withContextualElement() 參照 WebView 內相對於另一個 DOM 元素的範圍 DOM 元素。您應先呼叫 withElement(),建立參照 Web.WebInteraction 物件 (DOM 元素)。

    例子:

    Kotlin

    .withElement(findElement(Locator.ID, "teacher"))
        .withContextualElement(findElement(Locator.ID, "person_name"))

    Java

    .withElement(findElement(Locator.ID, "teacher"))
        .withContextualElement(findElement(Locator.ID, "person_name"));
  • check() 會評估條件,確保條件解析為 true

    例子:

    Kotlin

    onWebView()
        .withElement(findElement(Locator.ID, "teacher"))
        .withContextualElement(findElement(Locator.ID, "person_name"))
        .check(webMatches(getText(), containsString("Socrates")))

    Java

    onWebView()
        .withElement(findElement(Locator.ID, "teacher"))
        .withContextualElement(findElement(Locator.ID, "person_name"))
        .check(webMatches(getText(), containsString("Socrates")));
  • perform() 會在 WebView 中執行動作,例如點選元素。

    例子:

    Kotlin

    onWebView()
        .withElement(findElement(Locator.ID, "teacher"))
        .perform(webClick())

    Java

    onWebView()
        .withElement(findElement(Locator.ID, "teacher"))
        .perform(webClick());
  • reset() 會將 WebView 還原至初始狀態。如果先前的動作 (例如點擊) 導致導覽變更,使 ElementReference 和 WindowReference 物件無法存取,就必須執行這項操作。

    注意:雖然針對多頁面工作流程 (例如表單提交) 進行斷言時,使用 reset() 很有幫助,但測試範圍通常應有所限制,並著重於單一頁面。

    例子:

    Kotlin

    onWebView()
        .withElement(...)
        .perform(...)
        .reset()

    Java

    onWebView()
        .withElement(...)
        .perform(...)
        .reset();

範例

以下範例會測試在 WebView 中輸入文字並選取「Submit」按鈕後,相同文字是否會顯示在同一 WebView 的不同元素中:

Kotlin

const val MACCHIATO = "Macchiato"

@RunWith(AndroidJUnit4::class)
class MyEspressoWebTestSuite {

    @Test fun typeTextInInput_clickButton_SubmitsForm() {
        // Create an intent that displays a web form.
        val webFormIntent = Intent()
        // ...

        // Lazily launch the Activity with a custom start Intent per test.
        ActivityScenario.launchActivity(webFormIntent)

        // Selects the WebView in your layout. If you have multiple WebView
        // objects, you can also use a matcher to select a given WebView,
        // onWebView(withId(R.id.web_view)).
        onWebView()
            // Find the input element by ID.
            .withElement(findElement(Locator.ID, "text_input"))

            // Clear previous input and enter new text into the input element.
            .perform(clearElement())
            .perform(DriverAtoms.webKeys(MACCHIATO))

            // Find the "Submit" button and simulate a click using JavaScript.
            .withElement(findElement(Locator.ID, "submitBtn"))
            .perform(webClick())

            // Find the response element by ID, and verify that it contains the
            // entered text.
            .withElement(findElement(Locator.ID, "response"))
            .check(webMatches(getText(), containsString(MACCHIATO)))
    }
}

Java

public static final String MACCHIATO = "Macchiato";

@Test
public void typeTextInInput_clickButton_SubmitsForm() {
    // Create an intent that displays a web form.
    Intent webFormIntent = new Intent();
    // ...

    // Lazily launch the Activity with a custom start Intent per test.
    ActivityScenario.launchActivity(webFormIntent);

    // Selects the WebView in your layout. If you have multiple WebView objects,
    // you can also use a matcher to select a given WebView,
    // onWebView(withId(R.id.web_view)).
    onWebView()
        // Find the input element by ID.
        .withElement(findElement(Locator.ID, "text_input"))

        // Clear previous input and enter new text into the input element.
        .perform(clearElement())
        .perform(DriverAtoms.webKeys(MACCHIATO))

        // Find the "Submit" button and simulate a click using JavaScript.
        .withElement(findElement(Locator.ID, "submitBtn"))
        .perform(webClick())

        // Find the response element by ID, and verify that it contains the
        // entered text.
        .withElement(findElement(Locator.ID, "response"))
        .check(webMatches(getText(), containsString(MACCHIATO)));
}

其他資源

如要進一步瞭解如何在 Android 測試中使用 Espresso-Web,請參閱下列資源。

範例