Espresso-Web

Espresso-Web は、Android WebView UI コンポーネントを使って動作するエントリ ポイントです。 広く利用されている 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 Atom を再利用できるので、より柔軟なテストが可能です。特に、スタンドアロンの Web アプリと Android UI を含むアプリの両方を対象にテストを実施する場合に便利です。

仕組み

Espresso の onData() メソッドと同様に、WebView の操作はいくつかの Atom を通じて構成されます。Java プログラミング言語と JavaScript ブリッジの組み合わせを使って、WebView の操作を行います。Espresso が参照する内容は、Java をベースとした側ではすべて分離されたコピーとなっており、JavaScript 環境でデータが公開されることによる競合のおそれはありません。したがって、Web.WebInteraction オブジェクトからデータが完全な形で戻るようになっており、リクエストから返されるデータをすべて検証することが可能です。

WebDriver Atom とは

WebDriver フレームワークでは、プログラムでウェブ要素を検出して操作するために Atom が使用されます。WebDriver でのブラウザ操作は Atom を通じて実現しており、Atom は、ViewAction と同様のコンセプトに基づいて UI でのアクションを実行する自己完結型ユニットになっています。findElement()getElement() などの定義済みメソッドのリストを使用して Atom を表示させ、ユーザー視点でブラウザを動作させることが可能です。ただし、WebDriver フレームワークを直接使用する場合は、Atom を適切に連動させる必要があり、極めて詳細なロジックが要求されます。

Web クラスと Web.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. 依存関係内に次の行を追加します。

        androidTestImplementation 'androidx.test.espresso:espresso-web:3.1.0'
        
  3. Espresso-Web と互換性があるのは、Espresso 2.2 以降とテスト ライブラリのバージョン 0.3 以降のみであるため、次の行も必ず更新してください。

        androidTestImplementation 'androidx.test:runner:1.1.0'
        androidTestImplementation 'androidx.test:rules:1.1.0'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.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 のサポート

テスト実行時、システムによる WebView の操作はすべて JavaScript を使って行われます。したがって、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 にテキストを入力して [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)));
    }
    

参考情報

Espresso-Web を使用した Android のテストに関するその他の情報については、次のリソースをご覧ください。

サンプル

  • WebBasicSample: Espresso-Web を使用して WebView オブジェクトを操作します。