Espresso Web

Espresso-Web 是处理 Android WebView 界面组件的入口点。Espresso-Web 重复使用了热门 WebDriver API 中的 Atom,以检查和控制 WebView 的行为。

何时使用 Espresso-Web

使用 Espresso-Web 测试您的混合应用,特别是测试应用的原生界面组件与其 WebView 界面组件的集成情况。您可以将 Espresso-Web API 与其他 Espresso API 结合使用,以完全与 WebView 对象内的网页元素进行互动。

如果您只需要测试 WebView 本身,而不是测试 WebView 与应用中原生组件之间的互动,可以考虑使用 WebDriver 等框架来编写通用网页测试。如果您使用网页测试框架,则无需使用 Android 设备或 Java 虚拟机,这会使您的测试运行得更快、更可靠。话虽如此,Espresso-Web 允许您重复使用自定义 WebDriver Atom,这为您提供了很大的灵活性,尤其是在编写计划针对独立网页应用和包含 Android 界面应用运行的测试时。

工作原理

与 Espresso 的 onData() 方法类似,WebView 互动包含多个 Atom。WebView 互动使用 Java 编程语言和 JavaScript 桥的组合来完成其工作。由于公开来自 JavaScript 环境的数据不会引入竞态条件(Espresso 在基于 Java 的一侧看到的一切都是独立的副本),因此完全支持从 Web.WebInteraction 对象返回数据,从而允许您验证从请求返回的所有数据。

什么是 WebDriver Atom?

WebDriver 框架使用 Atom 以编程方式查找和操作网页元素。WebDriver 使用 Atom 来进行浏览器操作。从概念上讲,Atom 类似于 ViewAction,它是一个独立的单元,可在您的界面中执行操作。您可以使用一组定义的方法(例如 findElement()getElement())来公开 Atom,以便从用户的角度驱动浏览器。但是,如果您直接使用 WebDriver 框架,则需要正确编排 Atom,这需要相当详细的逻辑。

在 Espresso 中,类 WebWeb.WebInteraction 封装了这些样板代码,并为与 WebView 对象互动提供了类似于 Espresso 的感觉。因此,在 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. 在 dependencies 中添加以下行

    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。

您可以通过在被测 activity 中调用 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() 会引用 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 中输入文本并选择提交按钮后,相同的文本是否会出现在同一 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,请参阅以下资源。

示例