Airtestを使ってUIのテストを自動化する話

この記事は [Akatsuki Advent Calendar 2018](https://adventar.org/calendars/2990) の25日目の記事です。
 前回はZepprixさんの[開発環境インフラを ECS 移行している話](https://hackerslab.aktsk.jp/migrating-infrastructure-to-ecs)でした。

## この記事について
AirtestというUIテストツールを使ってテストを自動化しようという話です。
プロジェクトに導入したくて、どのように利用できるのかを調べたのでそのまとめになります。

## 経緯
スマホアプリ開発では運営が続くにつれて機能が増えていくため、検証コストが増加していくという問題があります。
そのため、基本的な機能の動作確認（チュートリアルなど）を可能であれば自動で行えるようにしたいと思ったのがきっかけです。(あと自分の興味)

## Airtestとは
[Airtest](https://github.com/AirtestProject/Airtest)はNetEaseが開発しているゲーム向けの画像認識によるUIテストツールです。 
AndroidとWindowsに対応しています。（iOSはベータ版）

## Airtestを使う
### 導入
今回は[AirtestIDE](http://airtest.netease.com/)を利用します。
リンク先からダウンロードしてインストールまで行なってください。
また、ADBが使える環境とAndroid端末を用意してください。

今回は[Maths Challenge](https://assetstore.unity.com/packages/templates/maths-challenge-59063)というUnityの完成プロジェクトをサンプルとしてテストを作成します。
まずは、上記のプロジェクトをビルドしてAndroid端末にインストールしてください。
その後、AirtestIDEを起動すると右上に`Mobile Phone Connection`というウィンドウがありAndroid端末を接続しているとその情報が表示されます。(接続しているけど表示されない場合は`refresh ADB`をクリック)
![スクリーンショット 2018-12-24 16.22.28.png](https://qiita-image-store.s3.amazonaws.com/0/153196/fefcf043-79da-b150-9262-a8a0d1fffa9c.png)

接続が正しくできている場合は`Action`に`connect`と表示されそこをクリックするとAndroidの画面が表示されるようになります。
![スクリーンショット 2018-12-25 22.51.51.png](https://qiita-image-store.s3.amazonaws.com/0/153196/89de26fe-dbbc-6420-ab8f-3e2048d0018e.png)


### テストを作成する
早速テストを作っていきましょう。
UIが表示されているか・されていないかのテストを作成します。
AirtestIDEの左にある`Airtest Assistant`ウィンドウにある`assert_exists`をクリックし
`Device Screen`に表示されている画面で`MATHS`と表示されている領域を選択します。
![スクリーンショット 2018-12-24 17.41.22.png](https://qiita-image-store.s3.amazonaws.com/0/153196/c95dbd3c-3130-0046-99b7-92416dd37cf6.png)
上記のコードが`Script Editor`に書かれたと思います。
これで`MATHS`が表示されているのかをチェックできます。
試しに実行してみましょう。
`Run Script`で実行して`Log viewer`に`OK`と出ていたらテスト成功です。
あとはこのタイトル画面の仕様に沿ってUIテストを作成していきます。
画像がないのでわかりにくいですが、自分は以下のようなテストを作成しました。

```Title.py
# -*- encoding=utf8 -*-¥
from airtest.core.api import *

# アプリをインストール
install("/Users/~/Downloads/Maths Challenge/sampleBuild.apk")

# アプリ起動
start_app("com.MadFireOn.MathsChallenge")

# 起動待機
sleep(5.0)

# タイトル画面のUIチェック
assert_exists(Template(file:///Users/~/Maths Challenge/AirTestScript/Title.air/tpl1544951184937.png, record_pos=(-0.001, -0.718), resolution=(1080, 1920)), "MATHSが表示されているか")
assert_exists(Template(file:///Users/~/Maths Challenge/AirTestScript/Title.air/tpl1544951192539.png, record_pos=(-0.003, -0.498), resolution=(1080, 1920)), "CHALLENGEが表示されているか")
assert_exists(Template(file:///Users/~/Maths Challenge/AirTestScript/Title.air/tpl1544951205702.png, record_pos=(-0.015, -0.013), resolution=(1080, 1920)), "PLAYボタンが表示されているか")
assert_exists(Template(file:///Users/~/Maths Challenge/AirTestScript/Title.air/tpl1544951215401.png, record_pos=(-0.001, 0.309), resolution=(1080, 1920)), "QUITボタンが表示されているか")
assert_exists(Template(file:///Users/~/Maths Challenge/AirTestScript/Title.air/tpl1544951294447.png, record_pos=(0.347, 0.732), resolution=(1080, 1920)), "設定ボタンが表示されているか")

# タイトル画面の初期状態ではオプションUIは表示されていないこと
assert_not_exists(Template(file:///Users/~/Maths Challenge/AirTestScript/Title.air/tpl1544951359799.png, record_pos=(-0.276, 0.732), resolution=(1080, 1920)), "Rateボタンが非表示になっているか")
assert_not_exists(Template(file:///Users/~/Maths Challenge/AirTestScript/Title.air/tpl1544951374244.png, record_pos=(-0.128, 0.738), resolution=(1080, 1920)), "Infoボタンが非表示になっているか")
assert_not_exists(Template(file:///Users/~/Maths Challenge/AirTestScript/Title.air/tpl1544951382645.png, record_pos=(0.026, 0.734), resolution=(1080, 1920)), "MoreGameボタンが非表示になっているか")
assert_not_exists(Template(file:///Users/~/Maths Challenge/AirTestScript/Title.air/tpl1544951395246.png, record_pos=(0.181, 0.73), resolution=(1080, 1920)), "Musicボタンが非表示になっているか")

# オプションUIの表示をチェック
touch(Template(file:///Users/~/Maths Challenge/AirTestScript/Title.air/tpl1544951294447.png, record_pos=(0.347, 0.732), resolution=(1080, 1920)))

assert_exists(Template(file:///Users/~/Maths Challenge/AirTestScript/Title.air/tpl1544951359799.png, record_pos=(-0.276, 0.732), resolution=(1080, 1920)), "Rateボタンが表示されているか")
assert_exists(Template(file:///Users/~/Maths Challenge/AirTestScript/Title.air/tpl1544951374244.png, record_pos=(-0.128, 0.738), resolution=(1080, 1920)), "Infoボタンが表示されているか")
assert_exists(Template(file:///Users/~/Maths Challenge/AirTestScript/Title.air/tpl1544951382645.png, record_pos=(0.026, 0.734), resolution=(1080, 1920)), "MoreGameボタンが表示されているか")

assert_exists(Template(file:///Users/~/Maths Challenge/AirTestScript/Title.air/tpl1544951395246.png, record_pos=(0.181, 0.73), resolution=(1080, 1920)), "Musicボタンが表示されているか")

touch(Template(file:///Users/~/Maths Challenge/AirTestScript/Title.air/tpl1544951294447.png, record_pos=(0.347, 0.732), resolution=(1080, 1920)))

# ホーム画面へ戻る
home()

# アプリをアンインストール
uninstall("com.MadFireOn.MathsChallenge")
```

### CLIで実行する
実際にプロジェクトでテストを実行する場合はIDEで実行するよりもCLIから実行できた方が他サービスとの連携が楽です。
CLIで実行する場合はAirtestIDEのAndroidとの接続を切ってください。（接続したままCLIから実行するとエラーが発生します）
また、MACとLINUXでCLIから実行する場合はパーミッションの変更が必要になります。

```shell
# for mac
cd {your_python_path}/site-packages/airtest/core/android/static/adb/mac
# for linux
# cd {your_python_path}/site-packages/airtest/core/android/static/adb/linux
chmod +x adb
```

CLIで以下のコマンドを実行するとテストが開始されます（Pathは各自の環境に合わせて置き換えてください）

```
airtest run "/Users/~/Maths Challenge/AirTestScript/Title.air" --device Android:///
```

`--device`は接続するデバイスを指定します。
`--device Android:///`だと接続しているAndroid端末になります。

### ログを出力しレポートを作成する
CLIで実行する際に`--log`オプションをつけるとログを出力してくれます。(AirtestIDEの場合はRun -> View Report)
Pathを指定しない場合は実行するスクリプトのディレクトリ内にログが出力されます。

また、`airtest report`コマンドで、出力されたログファイルからHTML形式のレポートを作成できます。

```
airtest report "/Users/~/Maths Challenge/AirTestScript/Title.air"
```
出力されたレポートの中身は以下のようになっており、各テストごとに結果が表示されています。
![スクリーンショット 2018-12-24 19.40.23.png](https://qiita-image-store.s3.amazonaws.com/0/153196/8f3101e4-b640-df5c-f4bd-4454950116a9.png)

### テスト内容を録画する
CLIで実行する際に`--record`オプションをつけるとテスト実行中に録画をしてくれます。
`--log`オプションも指定しないと動画は保存されないようなので、録画したい場合は`--log --record`をつけて実行してください。

## まとめ
Airtestを使って画像認識によるUIテストの自動化を試してみました。
画像認識によるUIのテストは何年か前に話題になりましたし、似たことができるツールとして魅力を感じました。
全てのUIテストを自動化するのは管理・維持のコストの面で現実的ではありませんが、人の手によって繰り返し行われるテストの一部を自動化するだけでも価値はあると思います。

## 参考サイト
https://airtest.readthedocs.io/en/latest/index.html
https://qiita.com/majimeneko/items/4e8d2bb9fee6a88a4921