AirtestでAndroidの連打テストを作成する

f:id:sumzap_engineer_blog:20200420100128p:plain

 サムザップ、CTO室所属の三森と申します。
最近はスマートフォン実機をPythonによって動作させる自動テスト等を担当しております、そのなかでも今回は対戦で遊び続けて動作が重くなったり、メモリリークが起きたりしないか?などのテストを行うためにどのような手法をとったか?などをざっくりと説明していきたいと思います。

背景について

 戦国炎舞 -KIZNA- は運用期間も長く、1つのアプリの中で様々なコンテンツやイベントが行われているゲームです。
そのなかでも「合戦」と呼ばれる、一日に3回行われる30分のチーム対人戦(いわゆるGVG)は戦国炎舞の重要なコンテンツとなっております。
各種計略、応援、必殺、奥義等タイミングや回数が重要になる場面が多々あります。
そのため表面上は問題なくても長時間起動や発熱、その他様々なコンテンツの状況が重なって、「ちょっと重くなる」ということが発生してしまうと合戦の結果に影響が出てしまうことがあるのです。
この、「ちょっと」というのが曲者で、30分の中でいつ発生するのか、なかなか検出し辛いという問題があります。

 そのため、実機で30分間、攻撃し続けるであるとか、ボタンを連打してバグ修正や、機能追加等によってバージョンを更新したアプリとその前のアプリによってコンボ数や結果に差がでないか?という試験を行いたいのですが、それを人間にまかせるというのはさすがに酷なものがあります。
そこで、Airtestを利用して連打テストを自動化してしまおうという手法を取りました。

手法について

 自動化にはAirtest(https://airtest.netease.com/)を利用しました。
Airetestは画像認識を利用した実機の自動テストをサポートしてくれるIDEやその環境群です。

 UIの場所や状態を正確に取得するためにPoco(https://github.com/AirtestProject/Poco)も利用します。
Airtestのような自動テストの中でも、特にUnityのGUIにフォーカスしたライブラリです。
PocoをUnityプロジェクトに組み込むことにより、GameObjectを参照できるようになります。

f:id:sumzap_engineer_blog:20200403181937j:plain

Airtestのツール周りを解説

 AirtestはPC,Macと接続してあるAndroid端末に対してテストを行いますが、その際、端末のスクリーンショットをPC側に転送。タップ位置等をPCからAndroid端末に送信しています。
これらはどうやって実現しているのでしょうか?Airtestを起動し、ログを見てみると起動時に、アプリケーションの転送を行っていることが確認できます。

minicap、minitouch

この2つはopenstf(https://github.com/openstf/ )と呼ばれるオープンソース(CyberAgentが公開しています!)の中のミドルウェアです

minicap.pyはAndroid内にminicap.soをインストール(ファイル転送)し、利用します。
https://github.com/AirtestProject/Airtest/blob/master/airtest/core/android/minicap.py#L99
このミドルウェアはスクリーンショットを円滑に取得するためにsocketを利用してスクリーンショットの転送を行っています。

minitouch.pyはAndroid内にminitouch.soをインストール(ファイル転送)し、利用します。
https://github.com/AirtestProject/Airtest/blob/master/airtest/core/android/minitouch.py#L26
こちらもsocketを利用して、タッチイベントの転送を行います。
こちらはAndroid内の入力データを書き換えることで高速に入力イベントを伝えることが可能となります。

課題

 AirtestがUIのポジションを認識するためにPocoというライブラリを利用しているのですが、こちらはAirtestを素直に使用しても人間と同等の速度を出すことはできません、というのもAirtestは

  • スクリーンショットの取得
  • 抽出した画像と一致する場所の探索
  • adbを利用して、その場所へクリックイベントを送信。

といった手順を踏んでおり、これをそのままテストに利用すると1タップ毎の時間がかかりすぎてテストになりません。
クリックする場所の検索は一度実行するだけで良いとして、それでもadbを利用したクリックイベントは速度に難ありです。

速度の解決策

 Airtestのコードとドキュメントを読み込んで行くと解決策があります。
こちらは、Android内のタッチイベントを制御するファイルを直接制御する手法です。
結論から書くと、ざっくりと以下のような変更を行いました。

#Before
poco = UnityPoco()
poco("LeftBgSprite").click()
#After
dev = device()
pos_attack_scale_x, pos_attack_scale_y = poco("LeftBgSprite").get_position()
pos_x = screen_width * pos_attack_scale_x
pos_y = screen_height * pos_attack_scale_y
dev.minitouch.touch((pos_x,pos_y))

UnityPoco()ではなくDevice()を利用し、minitouch(https://github.com/openstf/minitouch)を使ってタッチイベントを 素早く実機端末に伝えるようにすることで人間と同等な速度の連打テストを行えるようになりました。

まとめ

 Airtestを使い、最適化を行うことで、人間が行うような挙動のテストを自動で行えるようになりました。
しかし、こちらの手法はAndroidだけとなっておりiOSで実現するためにはまだ速度に課題が残っている状態です。
引き続き、iOSに向けての対応を行い、すべての実機環境で同様のテストを行っていくため日々開発を続けていきます。