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 を再利用できるため、特にスタンドアロン ウェブアプリと Android UI を含むアプリの両方に対して実行するテストを作成する場合には、高い柔軟性が得られます。

仕組み

Espresso の onData() メソッドと同様に、WebView インタラクションは複数の Atom で構成されます。WebView インタラクションは、Java プログラミング言語と JavaScript ブリッジを組み合わせて処理を行います。JavaScript 環境からデータを公開しても、競合状態が発生する可能性はないため、Espresso が Java ベース側で認識するものはすべて分離されたコピーです。Web.WebInteraction オブジェクトからのデータの取得は完全にサポートされており、リクエストから返されるすべてのデータを検証できます。

WebDriver Atom とは

WebDriver フレームワークは、Atom を使用して、プログラムでウェブ要素を検出、操作します。WebDriver でのブラウザ操作は Atom を通じて実現しており、Atom は概念的には、UI でアクションを実行する自己完結型ユニットである ViewAction に似ています。findElement()getElement() などの定義済みメソッドのリストを使用して Atom を公開し、ユーザーの視点からブラウザを操作します。ただし、WebDriver フレームワークを直接使用する場合、Atom を適切にオーケストレートする必要があり、非常に冗長なロジックが必要になります。

Espresso では、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. 依存関係内に次の行を追加します。

    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 の一般的な使用方法

onWebView() メソッドは、Espresso を使用して Android 上で WebView を操作する際のメインのエントリ ポイントです。このメソッドを使用して、次のような 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() を呼び出します。

@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() は、フォーム送信など、複数ページのワークフローに対してアサーションを行う場合に便利です。ただし、通常はテストのスコープを限定し、1 つのページに焦点を当てる必要があります。

    例:

    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 を使用する方法について詳しくは、以下のリソースをご覧ください。

サンプル

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