Airtest、一問一答。AirtestとPocoで始めるUnityアプリE2Eテスト


本記事は、サムザップ Advent Calendar 2019 #1 の12/20の記事です。

Airtestとは

https://airtest.netease.com/
NetEase社が開発し公開しているCross platform UI automation IDEです。
ここでは画像認識を利用したモバイルアプリの自動テストのIDEやプロジェクトの総称として呼ばせていただきます。これを利用することによって、プロジェクト上のコードを直接知らなくてもテストコードの作成が行なえます。E2Eテストの感覚でテストを量産することもできますし、エージングテストとして、同じ行動を無限に繰り返す。といった作業にも向いています。

Pocoとは

https://github.com/AirtestProject/Poco
Airtestのような自動テストの中でも、特にUnityのGUIにフォーカスしたライブラリです。
PocoをUnityプロジェクトに組み込むことにより、UITreeを参照できるようになります。

メリット

  • UIデザインが変更されてもテストが壊れない
  • 特定のゲームオブジェクトがあるか?を状態の判定に利用できる
  • 人間がやるには多すぎる手順があるときに機械に任せることができる
  • テストを作るに当たって、既存のコードに変更を加える必要がない

デメリット

  • 通常のテストに比べて面倒さはあるのでテストしたい内容は絞りましょう。
    • コード側の単体テストで担保できるならそれに越したことはないです。
    • 一方、エージングテストのような何回も繰り返す必要があるE2Eテストに関してはコスパが良いと考えます。

一問一答

Androidではどうやってうごいているか

https://github.com/AirtestProject/Airtest/tree/master/airtest/core/android
おもにここのソースコード郡を読み込んでいくと構造がわかります。

Android上にminicapを送り込み、minicapから画像を受け取るといったことやタップデータの送信等を行っています。
https://github.com/openstf/minicap
https://openstf.io/
ちなみにminicapはサイバーエージェントで公開しているプロジェクトの一つで、Android端末内で高速なスクリーンキャプチャの取得と送信を行ってくれます。

iOSではどうやって動いているか

libimobiledevice(https://github.com/libimobiledevice)
というツールで画像の取得等を行います。これはFlutterのiOSビルド等でも利用されているツールですね。さらに、タップ情報等ではXcTestを経由しています。

そのために、テスト用のプロジェクトが用意されていております。
いやーなかなかの力技ですね。でもこの方法ならホーム画面から端末の制御ができるのでE2Eテストとしてはなかなかですね。

Pocoの導入

https://github.com/AirtestProject/Poco-SDK/tree/master/Unity3D
こちらを導入して、利用していないGuiのディレクトリを削除します。

そして、こんな感じでマネージャーをアタッチしてあげると。

var pocoGameObject = new GameObject();
pocoGameObject.name = "Poco";
pocoGameObject.gameObject.AddComponent<PocoManager>();

動画を取りたい。

環境によりますが、Androidでは以下のAPIがその手助けをしてくれています。
UnitTestでの利用方法が実装のサポートとなるでしょう。
https://github.com/AirtestProject/Airtest/blob/master/airtest/core/android/recorder.py

UnitTestとして実行したい。

Pocoの動作が重い。

GameObjectの量が多く、さらに頻繁に位置情報のパラメータ等が変わるものがある画面では特に動作が重くなる傾向があります(例えばLive2Dでキャラクターを表示しているとか)
freezeの利用を検討しましょう。
https://poco.readthedocs.io/en/latest/source/doc/poco-example/optimize_speed_by_freezing_UI.html

freezeを利用するとfreeze()を使った瞬間のUIの場所を保持するため、アプリ側との疎通が発生せず
高速に入力を送信することができます。ただし、UIの場所が変わってしまう(スクロールするであるとか、移動するUI)ものに関してはfreeze()するタイミングや箇所を検討する必要があります。

self.freeze = self.poco.freeze()
# ボタンのUIはだいたい「動く」ことがないのでfreezeから取得する
# しかし、画面上に「あるか、ないか」の判断はできないことに注意すること。
self.attack_button = self.freeze("attack_button")

複数台を同時に制御したい。

# 単純に端末分指定すれば良い。
_serial   = "Android:///hogehoge"
dev       = connect_device(self._serial)
poco = UnityPoco(port, device=dev)

実際のプロジェクトではどのように利用しようとしていますか

プログラム知識がないQAの方にも使っていただけるように。CSV等に行動順を記述するような仕組みを作りました。

たとえば合戦を特定のルールに変更し。
特定の技を使ったあとにパラメータチェックする。などは以下のような記述になります。
あとはこのようなCSVを量産し、QAの方が手作業でやっていたようなチェックを自動化する段階です。

tag,param,param2
終戦,,ID
ルール変更,,四限極勢&治癒繚乱&独奏連律&怒髪追討&計略荒神&守護早成&天眼通力&奥義多才
開戦,,IDvsID
ゲーム遷移,hoge,ゲーム起動から合戦開始まで遷移
応援,不如帰,1
スプレッドシート入力,A2,none
ステータス変更,,ID&0
ステータス変更,,ID&20
応援,不如帰,1
スプレッドシート入力,B2,none

というわけでAirtestとPocoの紹介をお送りしました。発想次第では機械学習と組み合わせて、ボタンを細かく指定せずとも
デバッグ進行する。なんてこともできそうだな、取り組んでみたいなと考えております。いろいろ情報交換等できると幸いです。

明日は@mikami_akihisaさんの記事です。