uiautomatorのテキスト入力API「UiObject.setText()」は、テストを実行する端末の言語設定が英語で、かつ、IMEが英語入力モードでないと、想定とは異なる文字列が設定されてしまうことがあります。ここでは、その回避方法を紹介します。
UiObject.setText()は、仮想的なキーボード(PCで一般的に使われるQWERTYキーボード)をタイプし、対応するキーイベントを端末に送信することで、文字の入力を実現しています。
そのため、QWERTYキーボードでタイプできる文字しか、setText()を使って入力できず、日本語をはじめとする非ASCII文字を引数に指定しても、何も入力できません。
■非ASCII文字の入力を可能にするUiautomator Unicode Input Helper
テキスト入力フィールドに非ASCII文字を入力できるようにするためには、筆者が作成した「Uiautomator Unicode Input Helper」(以降では「UUIH」と書きます)が利用できます。
UUIHはApache License, Version 2.0のライセンスで提供されており、以下のURLからダウンロードできます。
UUIHは2つのモジュールで構成されています。
それぞれのモジュールのインストール方法と準備については、UUIHの「README.ja.md」の「準備」の節を参照してください。なお、サンプルのテストスクリプトには、すでにヘルパーライブラリを組み込んであります。
サンプルを動かしたい場合は、Utf7Imeだけをテストを実行する端末にインストールし、デフォルトのIMEに設定してください。
■テストスクリプトの修正
次に、テストスクリプト側で、UiObject.setText()で入力しようとしている文字列について、それぞれ以下のような修正を行います。
UiObject editText = ...; // テキスト入力フィールドを指すUiObject editText.setText("日本語");
UiObject editText = ...; // テキスト入力フィールドを指すUiObject String encodedText = Utf7ImeHelper.e("日本語") editText.setText(encodedText);
上記のように、直接setText()に入力文字列を指定する代わりに、ヘルパーライブラリが提供するUtf7ImeHelper.e()メソッドを使って変換した文字列を指定するようにします。Utf7ImeHelperクラスはjp.jun_nama.test.utf7ime.helperパッケージにあります。
なお、&以外のASCII図形文字しか入力しない箇所については、上記修正は必要ありません(修正しても問題ありません)。
UiObject.setText()は、テキスト入力フィールドが空でない場合に備えて、内部的にUiObject.clearTextField()を呼び出し、テキスト入力フィールドをクリアしてから、引数に指定された文字列を入力するようになっています。
ところが、端末の言語設定が英語以外になっていると、テキストフィールド入力フィールドのクリア処理が正しく動作しません。
例えば、端末の言語設定を日本語にすると、最初に紹介したCustomerModifyTest1クラスのテストも、顧客氏名を修正する箇所で失敗してしまいます。具体的には「John Doe 2」を「John Smith」に修正しようとすると、「John Smith Doe 2」となってしまいます。
uiautomatorは、テキスト入力フィールドをクリアするときに、以下の操作を行っています。
端末の言語設定を日本語にすると[Select all]メニューの名前(contentDescription属性)が[全て選択]に変化してしまうため、テキストを全て選択した状態にできず、最初の単語だけがクリアされてしまうのです。
この問題を解決するために、アクションバーのメニュー選択部分のロジックだけを修正した、以下のようなi18nClearTextField()メソッドを用意します。
/** * 引数に指定されたテキスト入力フィールドについて、すでに入力されている文字列をクリアする。 * テストを実行する端末の言語設定が英語以外でも動作する。 * * @param editText 内容をクリアしたいテキスト入力フィールド */ public static void i18nClearTextField(UiObject editText) throws UiObjectNotFoundException { editText.longClickTopLeft(); // "Select all"のリソース文字列を android.R.string.selectAll から取得する。 // この方法で、端末の言語設定に応じた文字列を取得できる。 String selectAllDesc = Resources.getSystem().getString(android.R.string.selectAll); UiObject selectAll = new UiObject(new UiSelector().descriptionContains(selectAllDesc)); if (selectAll.waitForExists(50)) selectAll.click(); SystemClock.sleep(250); UiDevice.getInstance().pressDelete(); }
UiObject.clearText()の代わりに、こちらを呼び出すことで、端末の言語設定が英語以外でも意図通りに動作するようになります。すでに文字列が設定されているテキスト入力フィールドに対してUiObject.setText()したい場合は、その直前に上記メソッドを呼び出してください。
サンプルのテストスクリプトでは、CustomerModifyTest3クラスに、この二つの問題に対応したテストを用意してありますので、参考にしてください。デフォルトのIMEをUtf7Imeにし、端末の言語設定を日本語にしてお試しください。
対応の要となるクラスは、DetailPageUnicodeSupportクラスです。このクラスはDetailPageクラスを継承し、「氏名」欄へ入力するtypeName()メソッドを、日本語対応版になるようにオーバーライドしています。
package com.nowsprinting.hellotesting.uiautomator.page; import com.android.uiautomator.core.UiObjectNotFoundException; import com.nowsprinting.hellotesting.uiautomator.util.CommonUiActions; import jp.jun_nama.test.utf7ime.helper.Utf7ImeHelper; /** * Detail画面を表すクラス(氏名欄のみUnicode文字の入力に対応)。 */ public class DetailPageUnicodeSupport extends DetailPage { @Override public DetailPage typeName(String name) throws UiObjectNotFoundException { if (name != null) { // 国際化対応版のclearTextField()を最初に呼び出す CommonUiActions.i18nClearTextField(mNameTextField); // Utf7Imeが理解できる文字列に変換してからsetTextする mNameTextField.setText(Utf7ImeHelper.e(name)); } return this; } }
2回にわたって、uiautomatorを紹介しましたが、いかがでしたでしょうか。uiautomatorは、他者が作成したアプリを操作できるという点では非常に強力なツールです。まだ開発途上の部分もありますが、今回紹介したノウハウを活用すれば、連載第1回で紹介した「システムレベルの機能テスト」で必要となる項目の多くを自動化できるはずです。
今回の記事では紹介しきれませんでしたが、アプリの強制終了時など、不意に発生する事象に対しても対応できる「UiWatcher」という仕組みも用意されています。筆者のブログに、使い方を紹介していますので、興味のある方は調べてみてください。
次回はBDD(Behavior-Driven Development/振る舞い駆動開発)の主要ツールの一つである「Calabash」を紹介します。
外山 純生
NTTソフトウェア株式会社勤務。数年前よりAndroidアプリケーション開発にかかわり始めたのを切っ掛けに、Androidにおける自動テストに興味を持つようになる。Android関連プロジェクトに対する技術支援業務に携りながら、Android向けのテストツールがもっと広く使われるようになることを願って、ブログにて技術情報を発信中
Blog:sumioの技術メモ
Twitter:@sumio_tym
Copyright © ITmedia, Inc. All Rights Reserved.