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 環境公開資料不太可能導入競爭狀況 (Java 端看到的所有 Espresso 看到的都是獨立的副本),因此從 Web.WebInteraction 物件傳回資料均完全受到支援,因此您可以驗證要求傳回的所有資料。

什麼是 WebDriver Atom?

WebDriver 架構使用 Atom 以程式輔助方式尋找和操控網路元素。WebDriver 會使用 Atom 來控制瀏覽器。Atom 在概念上與 ViewAction 這個獨立單元類似,可在您的 UI 中執行動作。您可以使用 findElement()getElement() 等定義方法清單公開 Atom,從使用者的視角驅動瀏覽器。不過,如果您直接使用 WebDriver 架構,則需要適當協調 Atom 才能要求使用較為冗長的邏輯。

在 Espresso 中,WebWeb.WebInteraction 類別會包裝這個樣板,並營造類似 Espresso 的體驗來與 WebView 物件互動。因此,在 WebView 中,Atom 會做為傳統 Espresso ViewMatchersViewActions 的替代項目。

接著,這個 API 看起來非常簡單:

Kotlin

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

Java

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

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

實作 WebView

請按照以下各節所述的指南,在應用程式測試中與 WebView 搭配使用。

套裝組合

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

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

    Groovy

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

    Kotlin

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

    Groovy

        androidTestImplementation 'androidx.test:runner:1.4.0'
        androidTestImplementation 'androidx.test:rules:1.4.0'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
        

    Kotlin

        androidTestImplementation('androidx.test:runner:1.4.0')
        androidTestImplementation('androidx.test:rules:1.4.0')
        androidTestImplementation('androidx.test.espresso:espresso-core:3.4.0')
        

常見的 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。

您可以呼叫 forceJavascriptEnabled() 做為受測試活動中的動作,強制啟用 JavaScript,如以下程式碼片段所示。

@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() 會參照相對於另一個 DOM 元素,在 WebView 中參照限定範圍的 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 中輸入文字並選取「提交」按鈕後,相同的文字是否會顯示在同一個 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,請參閱下列資源。

範例