自動化セッションを開始した直後の状態では、WebView内のUIコンポーネントを操作したり、状態を取得したりすることができません。
Appiumでは、UIコンポーネントの特定や検証の対象が「WebViewなのか」「そうでないのか(ネイティブなのか)」をコンテキストとして指定できるようようになっています(Androidの「Context」とは異なりますので、混同しないように注意してください)。
すなわち、WebViewコンテキストを指定すれば、WebView内のUIコンポーネントがその後の操作・検証の対象となり、ネイティブコンテキストを指定すれば、WebViewの外側にあるUIコンポーネントがその後の操作・検証の対象となります。
自動化セッションを開始した直後のコンテキストは「ネイティブ」になっているため、WebViewのテストをしない限り、コンテキストについて気にする必要はありません。テスト対象アプリにWebViewが含まれており、WebView内部に表示されている内容(HTML)を操作・検証したい場合にコンテキストの指定が必要になります。
それぞれのコンテキストは文字列で表現され、「"NATIVE_APP"」が「ネイティブ」を表し、それ以外の文字列が「WebView」を表します。
コンテキストの指定には、自動化セッション開始時に生成したAndroidDriverインスタンスの「context()」メソッドを使います。
また、「getContextHandles()」メソッドで、現在指定することのできるコンテキストの一覧を取得できます。
この2つのメソッドを使うことで、以下のようなコードでWebViewコンテキストに切り替えることが可能です。
例えば、サンプルのテストスクリプトでは、「PreviewPage#ensureWebViewContext()」メソッドで、下記のようにWebViewコンテキストの指定を行っています。
public class PreviewPage { // setUp()で生成したAndroidDriverインスタンス protected AndroidDriver mDriver; // (省略) /** 現在のコンテキストがWebViewコンテキストであるかどうかを判定する。*/ private boolean isInWebViewContext() { String currentContext = mDriver.getContext(); return isWebViewContext(currentContext); } /** * 指定されたコンテキストがWebViewコンテキストであるかどうかを判定する。 * 文字列が"NATIVE_APP"で<b>なければ</b>、WebViewコンテキストであると判断する。 */ private boolean isWebViewContext(String context) { return !context.equals("NATIVE_APP"); } /** * WebViewコンテキストに切り替える。 * すでにWebViewコンテキストの場合は何もしない。 */ private void ensureWebViewContext() { if (isInWebViewContext()) { // すでにWebViewコンテキストの場合は何もしない。 return; } for (String context : mDriver.getContextHandles()) { if (isWebViewContext(context)) { mDriver.context(context); return; } } Assert.fail("WebView context doesn't exist"); } }
WebView内のUIコンポーネントを特定する場合の検索条件について、Appiumのドキュメントには明確な記載はありません。
ただ、WebViewのリモートデバッグ機能がChromeDriverを利用していることから、ChromeDriverがサポートしている範囲(「By」クラスで提供されている範囲)であれば、問題なく動作するはずです。
例えば、サンプルのテストスクリプトでは、「PreviewPage#getMailAddress()」メソッドで、Preview画面(WebViewで実装されています)に表示される顧客のメールアドレスを取得しています。
public String getMailAddress() { ensureWebViewContext(); // WebViewコンテキストにする return mDriver.findElement(By.name("mail")).getText(); }
Preview画面のWebViewで表示されるHTMLは、以下のような形式ですから、上記のコードは、「name」属性が「mail」である要素内のテキストを取得していることになります。
<html> <!-- (省略) --> <body style="background-color:transparent; -webkit-user-select:none;"> <h4 name="name">(ここに顧客氏名が入る)</h4> メール<div name="mail">(ここにメールアドレスが入る)</div></br> 性別<div name="gender">(ここに性別が入る)</div></br> 年齢<div name="age">(ここに年齢が入る)</div></br> 区分<div name="division">(ここにマーケティング区分が入る)</div> </body> </html>
Selendroid modeでも、UIコンポーネントの特定から操作までの流れはAppium modeと同じですが、利用できる検索条件が、Appium modeとは異なります。
Selendroid modeで利用できる検索条件については、Selendroidの公式ドキュメントを参照してください。
検索条件の指定方法をAppium modeとSelendroid modeで比較すると、WebViewコンテキストでは両者はほとんど同じですが、ネイティブコンテキストでは「By.className()」以外は全て異なっています。そのため、ネイティブコンテキストでは検索条件の共用は諦めた方が無難です。
サンプルでは、Selendroid mode向けのテストスクリプトを「com.nowsprinting.hellotesting.appiumtest.selendroid」パッケージ配下に配置しています。Appium modeのPage Objectクラスを継承することでAppium modeとの差分のみが分かるようになっていますので、書き方の違いを確認する際の参考にしてください。
自動化セッション開始時に生成した「AndroidDriver」インスタンスの「rotate()」メソッドを呼び出すことで、端末の画面を回転させることができます。
引数に指定できる画面の向きは「ScreenOrientation.LANDSCAPE」と「ScreenOrientation.PORTRAIT」のみです。uiautomatorのような90度単位での回転はできません。
このメソッドを呼び出すと、自動的にセンサーによる画面の自動回転も無効になります。
自動化セッションを終了すると、再度自動回転が有効になるため、後続のテストへの影響を避けるために、tearDown時に必ず「quit()」メソッドを呼ぶようにしてください。
自動化セッション開始時に生成した「AndroidDriver」インスタンスの「getScreenshotAs()」メソッドを呼ぶことで、画面のスクリーンショット(PNG形式)を撮影できます。
メソッドの引数には、撮影したスクリーンショットをどのような形式で受け取るかを指定します。指定できる値は以下の3つです。
撮影したスクリーンショットをファイルとして残しておきたい場合は、Apache Commons IOの「FileUtils.writeByteArrayToFile()」を利用して、以下のようにするのが簡単です。
AndroidDriver mDriver; // 自動化セッション開始時に生成したAndroidDriver File f; // スクリーンショットを保存したいファイル名 byte[] screenshot = mDriver.getScreenshotAs(OutputType.BYTES); FileUtils.writeByteArrayToFile(f, screenshot);
なお、本稿執筆時点では、WebViewコンテキストだとスクリーンショットの撮影に失敗します(ChromeDriverのIssue 792)。当面は、ネイティブコンテキストに戻してからスクリーンショットを撮影することで回避してください。
Jenkinsなどを使ってCI(継続的インテグレーション)を実現するためには、Appiumサーバーもコマンドラインから実行できる必要があります。現状、Appium Desktop Appに内包されているAppiumサーバーをコマンドラインから実行することはできないため、コマンドライン版を別途セットアップする必要があります。
コマンドライン版のAppiumは、node.jsのパッケージ管理ツールnpmを使ってインストール・実行できます。
Mac OS XでHomebrewを利用しているのであれば、以下のコマンドを実行することでnode.jsとnpmをインストールできます。WindowsやLinuxへのインストール方法についてはInstalling Node.js via package managerを参照してください。
$ brew install node
npmがインストールされれば、以下のコマンドでAppiumサーバーをインストールできます。
$ npm install -g appium
インストールされた「appium」コマンドを実行することで、Appiumサーバーを開始できます。
Appium GUI版の「Android Settings」などで設定できた、Appiumサーバー起動時の設定項目は、コマンドラインオプションで指定します。
例えば、テスト対象のapkファイルのフルパスは、「--app」オプションを使って以下のように指定します。
$ appium --app {プロジェクトのルート}/app/build/outputs/apk/app-appium-debug.apk
指定できるコマンドラインオプションの詳細はServer flagsを参照してください。
今回は、テストスクリプトの書き方を一通り紹介しました。
記事中でも触れましたが、現時点ではUIコンポーネントの特定方法についてのドキュメントが少ないため、本稿の内容がテストスクリプトを書くときの参考になれば幸いです。
次回は「Appium応用編」として、Turnipを組み合わせてBDDを実践する方法について解説します。
NTTソフトウェア株式会社勤務。数年前よりAndroidアプリケーション開発にかかわり始めたのを切っ掛けに、Androidにおける自動テストに興味を持つようになる。Android関連プロジェクトに対する技術支援業務に携りながら、Android向けのテストツールがもっと広く使われるようになることを願って、ブログにて技術情報を発信中
Blog:sumioの技術メモ
Twitter:@sumio_tym
Copyright © ITmedia, Inc. All Rights Reserved.