読者です 読者をやめる 読者になる 読者になる

Hack the World!

プログラミングや,ネットワークに関する話題を取り扱っています.知識をつけて,優雅にお仕事するのを目指しています.

Android APIレベル17から使えそうな、UI Automator Testについて調べてみた(その1)

*1

AndroidSDKソースコードを読んでいるときに発見しましたが、
APIレベル16(Jellybean)からは、UIを操作して実行するテストに、新しい方法が追加されるようです。*2


UI Automator Testです。

UI をユーザが操作するテストには、これまでは MonkeyRunner が有りましたが、MonkeyRunnerは、pythonで記述するなど、Javaしか知らないユーザには敷居が高いと言われていました。
# 個人的にはpythonの方が好きなのですが

Javaから、利用する方法もあるには有りましたが、公式な扱いではないため(普通の人がJava での利用法にたどり着くのは)容易ではありません。
今回追加された方法は、Javaのコードを記載することで利用可能です。

また、MonkeyRunnerは、ホスト側のPCと常に通信を行うため、ターゲット側との通信にリソースを取られていました。
しかし、今回追加された方式では、JUnitを使ったテスト同様、端末(ターゲット)にコードを事前に送り込んで実行します。



さて、利用方法を見ていきましょう。

コードレベルの利用方法を記載したドキュメントは、まだありません。
ソースコードを読む必要があります。

また、現状では、Android 全体のソースコードが必要です。

/frameworks/testing/uiautomator/

  • cmds : adb shell から実行するためのコマンド
  • library: テストフレームワークのライブラリ
  • samples: サンプルのスケルト
  • utils : DummyIME(用途不明、おそらくはテスト実行時の際に利用するIME)


ビルドと実行の方法は、samples/SkeletonTest 配下にある README を読んでください。

デフォルトでは、uiautomator はビルドの対象にならないので、
Android のビルド後に以下のコマンドを発行することで、ビルドされます。

mmm frameworks/testing/uiautomator/samples/SkeletonTest

mmm は、Android のビルドの envsetup.sh を読み込むと使えるようになるコマンドです。
上記コマンドを実行することで、「uiautomator.skeletontest.jar」というファイルが生成されます。

これを、ターゲットの /data/local/tmp/ に配置してやり、コマンドを発行することでテストを実行します。

adb push {DROIDROOT}/out/data/local/tmp/uiautomator.skeletontest.jar /data/local/tmp/
adb shell uiautomator runtest uiautomator.skeletontest.jar \
-e class com.android.uiautomator.samples.skeleton.DemoTestCase

                                                                                            • -

としてみたところ、以下のようにRuntimeErrorが発生してしまいました。
原因はまだよくわかっていません。
TestCaseを集める所辺りでエラーが起こっているようです。

C:\Users\miyabi\testing>adb shell uiautomator runtest uiautomator.skeletontest.jar -e class com.android.uiautomator.skeletontest.DemoTestCase
INSTRUMENTATION_RESULT: shortMsg=java.lang.RuntimeException
INSTRUMENTATION_RESULT: longMsg=com.android.uiautomator.skeletontest.DemoTestCase
INSTRUMENTATION_CODE: 0

まだ先は長いようです。

                                                                                            • -

自分でテストケースを作る場合は、上記のスケルトンをコピーしてモジュール名を書き換えてやれば良さそうです。

Android.mkの LOCAL_MODULE_NAME, LOCAL_MODULE_TAGSを書き換えましょう。

                                                                                            • -

サンプルの解説

サンプルは非常に簡単なもので、以下の操作を再現しているようです。
1.ホームボタンを押し機能したかを確認
2.画面サイズを取得
3.ステータスオブジェクトに文字列を追加し、渡した先のFakeInstrumentWatcher経由でSystem.printlnする。

assertTrue(getUiDevice().pressHome());
Bundle status = new Bundle();
status.putString("msg", "This is a demo test and I just pressed HOME");
status.putString("product", getUiDevice().getProductName());
Point p = getUiDevice().getDisplaySizeDp();
status.putInt("dp-width", p.x);
status.putInt("dp-height", p.y);
getAutomationSupport().sendStatus(Activity.RESULT_OK, status);

                                                                                            • -

UI Automator Testにて使える関数

UiAutomatorTestCaseは、JUnitのTestCaseの派生の為、JUnit同様のassertが利用可能です。
また、テスト環境にも様々なクラスが用意されています。

その中で、重要そうなクラスをピックアップしてみました。
今後、使い方を模索しながら試してみたいと思います。

  • UIDevice : Android端末を意味するオブジェクト、画面サイズやハードウェアキー、クリック(座標系)
  • UIObject : Android 上のViewに該当するオブジェクト、クリックなどの操作が可能
  • UISelector : Viewを探すための指定子

インデックス(何の?)、含まれているテキストを検索、クラス名、フォーカス、スクロール可能等の指定によって、
        Viewを指定可能にする者と思われます。


UIDeviceが、MonkeyDevice 相当の様です。
あまり解説はされてきてませんが、EasyMonkeyDevice 関連の実装で実現しようとしていたことを、
MonkeyViewが、UIObject相当で実現しているようです。
monkeyrunner.easy.By での検索は遅かったので、UISelectorの検索速度に期待したい所です。
# EasyMonkeyDeviceは、HierarchyViewerと連携していたので遅かった・・・のでこれまであまり解説して来ませんでした。

                                                                                            • -

その他出来ること

adb shell uiautomator には、他にもサブコマンドが用意されています。

events と dump です。
events はユーザが実際にターゲット端末を操作したイベントをホストPCに転送します。
PC側のリッチな環境でイベントの解析を行い、リプレイを行うなどの機構が用意されれば、MonkeyRecorder 同様の操作が可能になります。
また、手動でテストを行う際のエビデンスとしても利用可能です。

また、dump は、Hierarchy Viewerの代替です。
Hierarchy Viewerは、Engineering ビルドをした端末か、エミュレータでしか利用できませんでしたが、
dumpは全ての端末で利用可能になる物と思われます。
# アプリ側で制限がかけられるといいですね。

上記の図のように、Eclipse から実行する機能が追加されており、dumpしたXMLEclipseが解析することで、
UIの構造を読み取ることが可能です。

adb shell uiautomator dump を実行した場合は、/data/local/tmp/window_dump.xml が生成されます。
# 名前は変えられない模様
# XMLを動的に読み取れば、テストの自動生成が出来るかとも思ったのですが、IDがないため難しそうです。

                                                                                            • -

気になっている所

Report はどのように通知されるのか。
JUnitXML形式への変換は行えるのか。
# Jenkins での判別に使えるので

UIAutomatorTestCase自体が、JUnitのTestCaseの派生なので、JUnit のライブラリを弄れば使えそうです。


総括

                                                                                            • -

MonkeyRunner同様にUIの操作が可能と思われるAPIが用意されています。
しかしながら、動作しないこととテストの作成にAndroid 自体のビルドを行わねばならぬこと等、まだまだ敷居が高い様です。

継続的に監視します。

                                                                                                      • -

追記:ClassNotFoundなので、ちゃんとロードできてないのが原因?
classpath からなにから追って見る。

E/UiAutomatorTestRunner( 794): uncaught exception
E/UiAutomatorTestRunner( 794): java.lang.RuntimeException: com.android.uiautomator.skeletontest.DemoTestCase
E/UiAutomatorTestRunner( 794): at com.android.uiautomator.testrunner.UiAutomatorTestRunner.start(UiAutomatorTestRunner.java:98)
E/UiAutomatorTestRunner( 794): at com.android.uiautomator.testrunner.UiAutomatorTestRunner.run(UiAutomatorTestRunner.java:85)
E/UiAutomatorTestRunner( 794): at com.android.commands.uiautomator.RunTestCommand.run(RunTestCommand.java:76)
E/UiAutomatorTestRunner( 794): at com.android.commands.uiautomator.Launcher.main(Launcher.java:83)
E/UiAutomatorTestRunner( 794): at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
E/UiAutomatorTestRunner( 794): at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:235)
E/UiAutomatorTestRunner( 794): at dalvik.system.NativeStart.main(Native Method)
E/UiAutomatorTestRunner( 794): Caused by: java.lang.ClassNotFoundException: com.android.uiautomator.skeletontest.DemoTestCase
E/UiAutomatorTestRunner( 794): at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:61)
E/UiAutomatorTestRunner( 794): at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
E/UiAutomatorTestRunner( 794): at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
E/UiAutomatorTestRunner( 794): at com.android.uiautomator.testrunner.TestCaseCollector.addTestClass(TestCaseCollector.java:84)
E/UiAutomatorTestRunner( 794): at com.android.uiautomator.testrunner.TestCaseCollector.addTestClass(TestCaseCollector.java:72)
E/UiAutomatorTestRunner( 794): at com.android.uiautomator.testrunner.TestCaseCollector.addTestClasses(TestCaseCollector.java:53)
E/UiAutomatorTestRunner( 794): at com.android.uiautomator.testrunner.UiAutomatorTestRunner.start(UiAutomatorTestRunner.java:95)
E/UiAutomatorTestRunner( 794): ... 6 more
I/AndroidRuntime( 794): VM exiting with result code -1.

*1:2012/10/22時点でまだ動いてる所を確認できていません。

*2:17かも?