Espresso Web

Espresso-Web 是一个与 Android WebView 界面组件一起使用的入口点。Espresso-Web 重复使用常用 WebDriver API 中的 Atom 来检查和控制 WebView 的行为。

何时使用 Espresso-Web

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

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

了解运作方式

与 Espresso 的 onData() 方法类似,WebView 交互包含多个 Atom。WebView 交互结合使用 Java 编程语言和 JavaScript 桥接来完成其工作。由于公开 JavaScript 环境中的数据不会引入竞态条件(Espresso 在基于 Java 的端看到的所有内容都是隔离的副本),因此系统完全支持从 Web.WebInteraction 对象返回数据,让您可以验证从请求返回的所有数据。

什么是 WebDriver Atom?

WebDriver 框架使用 Atom 以编程方式查找和操控网页元素。Atom 由 WebDriver 用来进行浏览器操纵。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. 在依赖项内添加以下代码行:

    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() 作为被测 activity 中的操作来强制启用 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 并选择 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,请参阅以下资源。

示例