Upgrade to Pro — share decks privately, control downloads, hide ads and more …

AirtestとPocoとOpenSTFによるUnity製スマートフォン向けゲームの実機自動テスト環境構築とその利用方法

 AirtestとPocoとOpenSTFによるUnity製スマートフォン向けゲームの実機自動テスト環境構築とその利用方法

「CEDEC 2020」の発表資料です。
http://cedec.cesa.or.jp/2020/session/detail/s5e82a55322244

CyberAgent SGE Engineer

September 04, 2020
Tweet

More Decks by CyberAgent SGE Engineer

Other Decks in Technology

Transcript

  1. AirtestとPocoとOpenSTFに
    よるUnity製スマートフォン向け
    ゲームの実機自動テスト
    環境構築とその利用方法
    CEDEC2020 株式会社サムザップ 三森裕史

    View full-size slide

  2. 今回の話の概要
    ・実機で自動テストをしよう!端末は手元になくてもOK
    ・「テスト」がないのですぐにできるテストを構築したい
    ・非プログラマーでも自動化したい
    ・コロナ禍であってもだれもが同じ端末でチェックできたら良いよね
    ・甘口セッションです Python知らなくても大丈夫
    Androidの話が殆どですが、iOS周りの動向も補足します
    2

    View full-size slide

  3. 自己紹介
    携帯電話やスマートフォンの上で動くゲームを作る人
    最近はテストやツール周り等
    2017年サムザップ入社
    CTO室所属 三森 裕史
    過去の発表等
    builderscon2018
    3

    View full-size slide

  4. 株式会社サムザップ
     2009年5月 CyberAgent子会社として設立
    事業内容
     スマートフォンゲームの企画、配信、運営
    会社概要
    4

    View full-size slide

  5. 戦国炎舞 -KIZNA-
    2013年4月サービス開始
    戦国時代を舞台にしたカードゲーム
    運営開始から7年が経過
     検証項目の増大が課題
    5

    View full-size slide

  6. この仕組みを導入すると何ができるか
    実機連打テストの自動化!
    6

    View full-size slide

  7. この仕組みを導入すると何ができるか
    CSVファイル等を利用しノンプログラマでも自動テストが作れる仕組み
    7

    View full-size slide

  8. 新規タイトルでの運用
    ゲームを遊び続けて、エラーが出力された際にSlackへ通知
    コンポーネントの中身の値チェック
    指定された箇所の動画、スクショ撮影、保存
    8

    View full-size slide

  9. 今回使用するツールの紹介と説明
    9
    Airtest Poco
    DeviceFarmer
    (OpenSTF)

    View full-size slide

  10. Airtestについて
    https://airtest.netease.com/
    ・NetEase社が開発した画像認識によるテストコードを記述できる環境 とライ
    ブラリ群
    ・IDEによって気軽にテストコードが記述できる
    ・テストコードはPythonのコードとして保存され、Jenkinsのような各 種CI環
    境で実行することが可能
    10

    View full-size slide

  11. Airtestについて
    11

    View full-size slide

  12. Airtestでできること
    ・Pythonによるテストコードを記述できる
     主に画像認識ベースの処理
    ・タッチ、スワイプ等の処理
     切り抜いた画像でテンプレートマッチングし、合致した箇所にタップ
    ・各種assert
     画面内に切り抜いた画像と一致する箇所があるか?
    12

    View full-size slide

  13. Airtestでできること
    13

    View full-size slide

  14. Pocoについて
    Airtestのライブラリ
    Unityや各種ゲームフレームワークの
    UI情報を取得し、Python上で操作
    することができる。
    14

    View full-size slide

  15. Pocoでできること
    Unityや各種ゲームフレームワークのUI情報を取得
    15

    View full-size slide

  16. DeviceFarmerについて
    16
    同一ネットワーク下でブラウザごしに実機を操作できるプロダクト
    元々はCyberAgent社で開発していた端末管理のOSS
    現在はOpenSTFからDeviceFarmerというプロジェクトに名称変更
    Web上から端末の操作(APKインストールやWebブラウザ操作、
    エクスプローラー等)が可能

    View full-size slide

  17. DeviceFarmerについて
    17

    View full-size slide

  18. DeviceFarmer 端末リスト
    18

    View full-size slide

  19. DeviceFarmer 端末画面
    19

    View full-size slide

  20. 余談:CyberAgentでは
    沖縄に設置されており、ネットワーク越しに
    各子会社が利用できるようになっています
    20

    View full-size slide

  21. じゃあ、それらを組み合わせると?
    ステイホームしながら、指定の端末でテストができる
    21

    View full-size slide

  22. 導入方法から、テスト作成まで
    22

    View full-size slide

  23. 導入方法 Airtest IDEのインストール
    https://airtest.netease.com/ 23

    View full-size slide

  24. 導入方法 Unity側にPocoSDKを導入
    https://github.com/AirtestProject/Poco-SDK
    1.ライブラリのダウンロード
    2.使用しているUIに合わせてファイルを削除
    3.Unityのプロジェクトに導入
    4.PocoManagerをScene上に設置する
    25

    View full-size slide

  25. PocoSDK
    プロジェクトが利用するGUIシステム
    にあわせて、ファイルの除去を行う
    26

    View full-size slide

  26. PocoSDK
    プロジェクトがuGUIを採用していたら
    NGUI,fairyguiを削除してUnityへインポート
    27

    View full-size slide

  27. PocoManager
    導入
    #if ENABLE_AIRTEST
    // Airtestデバッグの為に実体化
    var pocoGameObject = new GameObject();
    pocoGameObject.name = "Poco";
    pocoGameObject.gameObject.AddComponentManager>();
    #endif
    シーン上に予めPocoManagerを
    配置しても動作するが、
    シンボルで切り分けた方が
    取り回しが良い
    28

    View full-size slide

  28. Device Farmer
    の導入
    node.js環境か、docker-component環境で構
    成してる情報の方が多く、手間も少ないのでおす
    すめ
    時間の都合上割愛しますがDocker環境を整え
    て、docker-composeファイルを用意し
    $ docker-compose up
    資料末の参考資料に参考にさせ
    て頂いて、実際に起動させた
    docker-composeの参照先を記
    載しております
    29

    View full-size slide

  29. 起動画面
    30
    いきなりログイン画面を要求されますが適当でOK
    セキュリティを担保したい場合の処理は後述

    View full-size slide

  30. 準備中から進まない?
    ・ログを確認しよう
    ・起動させる前にUSB接続済み?
    ・https://github.com/openstf/stf/issues/407
    ・https://github.com/openstf/stf/issues/670
    導入時インストールされるstf.apk周りの動作チェック
       stfアプリをadbで起動させる等の対処法あり
    31

    View full-size slide

  31. ワークフロー
    開発者はPocoSDK
    をUnityアプリに組み込む
    32
    Pocoが組み込まれた
    APKを出力

    View full-size slide

  32. ワークフロー
    テスト製作者はDeviceFarmer
    にアクセスし、端末を確保
    33
    APKをインストール
    端末一覧から端末をクリックす
    ると「使用状態」になり
    他の人が使えないように

    View full-size slide

  33. ワークフロー
    AirtestIDEを利用して
    UI情報を取得
    34
    adb connect した端末は自分
    の手元にある端末と同様に
    あつかえる
    誰でも実行可能なように
    Gitリポジトリに格納。

    View full-size slide

  34. ワークフロー
    35
    作成したテストはPythonコード
    として、Jenkins等の実行環境

    Jenkins側はテスト実行時に
    DeviceFarmerから端末を確保
    し、テストを実行する
    新規バイナリか、テストコードの
    更新がある度に実行する
    結果をSlack等に通知

    View full-size slide

  35. テストコードを書く
    from airtest.core.api import start_app
    from poco.drivers.unity3d import UnityPoco
    # ゲームを起動し、Pythonコードを実行すると
    # ゲーム内PocoSDKとPythonが接続される。
    poco = UnityPoco()
    # 特定のゲームオブジェクトのタップ
    poco("BattleButton").click
    # 長押し処理
    poco.long_click(pos=[0, 0], duration=3.0)
    # テキスト情報の取得
    text = poco("ErrorCode").get_text()
    36

    View full-size slide

  36. 取得できる情報
    # UIの名前の取得
    poco("CoalitionName").get_name()
    # UIの座標取得
    poco("CoalitionName").get_position()
    # テキスト値の取得
    poco("CoalitionName").get_text()
    # 体力を確認して、その値が0以下じゃなければ
    # みたいな処理をよく書いたりする
    hp = poco("HitPoint").get_text()
    if hp not 0:
    37

    View full-size slide

  37. 38
    エラーがあったら
    Slackに通知
    def exists_end_report(self, poco):
    if poco("ErrorCode").exists():
    image = poco.snapshot()
    text = poco("ErrorCode").get_text()
    self.messenger.upload("screen." +
    image[1], text, base64.b64decode(image[0]))
    sys.exit()
    画面上にエラーコードが発行さ
    れていたらslackに投稿する

    View full-size slide

  38. 39
    指定のスキルが見つ
    かるまで遷移
      def skill_table_pos_check(self, skill_name):
    pos = self.poco(text=skill_name).get_position()
    # 画面外に押したいボタンがある
    while pos[0] < 0 or pos[0] > 1:
    # スワイプさせたり、ボタンを押したり等の処理
    if pos[0] < 0:
    self.poco("LeftArrowButtonSprite").click()
    if pos[0] > 1:
    self.poco("RightArrowButtonSprite").click()
    pos = self.poco(text=skill_name).get_position()

    View full-size slide

  39. 40
    動画撮影 poco.device.start_recording()
    # test.mp4で動画ファイルを出力
    poco.device.stop_recording(output="test.mp4")
    # 中断させる場合はis_interrupted=True
    poco.device.stop_recording(is_interrupted=True)

    View full-size slide

  40. 「自動化」に向けてもうひと工夫
    41

    View full-size slide

  41. Python環境側
    ・Airtest内で完結するなら .air
    ・Pythonコードとして動作させるなら .py
    Pythonの各種ライブラリの取り回しを
    よくしたいのであればpure pythonで
    構築するのがおすすめ
    42

    View full-size slide

  42. DeviceFarmerとAirtestの連携
    端末画面から、リモートデバッグコマンドをコピペして利用する。
    これも自動化したい。
    43

    View full-size slide

  43. Device Farmer 端末の確保
    Device FarmerはOpenAPI対応なので好みの言語でClientを生成する
    ・https://github.com/swagger-api/swagger-codegen
    もしくはJenkinsのプラグインを利用する
    ・https://plugins.jenkins.io/open-stf
    44

    View full-size slide

  44. 端末確保の自動化
    def main(adb_path, serials):
    api_instance = create_api_instance()
    devices = api_instance.get_user_devices()
    print(devices)
    for serial in serials:
    print(serial)
    device = swagger_client.AddUserDevicePayload(serial)
    add_user_device(api_instance, device)
    thread = api_instance.remote_connect_user_device_by_serial(serial,
    async_req=True)
    res = thread.get()
    remote_connect_url = res.remote_connect_url
    cmd = adb_path + " connect " + remote_connect_url
    while True:
    proc = subprocess.run(cmd, shell=True, stdout=PIPE, stderr=PIPE,
    text=True)
    time.sleep(1)
    if 'connected' in proc.stdout:
    break
    swagger-codegenを利用し
    て、Pythonでクライアントを作

    45

    View full-size slide

  45. ツール利用と組み合わせのまとめ
    Pocoを組み込み、Airtestでテストを作成、DeviceFarmer環境でテストを行う
    ・結果はSlackやスプレッドシートに送信
     あまりUnitTestのような感覚はなくE2Eで正しく動くか?が大きい
    ・recording機能で動画を保存できるようにしておく エビデンスとして
    46

    View full-size slide

  46. UI名を利用したテストの利点
    デザインが変わってもPocoは「PlayButton」
    で位置情報を取得できる
    PlayButton
    47
    Poco(“PlayButton”).click()

    View full-size slide

  47. テストを作成するチームと開発チームを分離できる
    テストを作る側は開発チームのコードとは独立したテストコードを作成できる 
    チームの分離が可能
    48
    C#知らなくても
    テストが作れる
    テスト製作者に
    配慮せず設計変更

    View full-size slide

  48. 使用事例
    49

    View full-size slide

  49. 要件
    戦国炎舞はGVGのゲーム
    トップ層はシビアな戦いを行っている
    バージョンの差異等でレスポンスの悪化があると
    非常によろしくない
    とはいえ、毎回人間がレスポンスを確認するのは…
    ということで
    50

    View full-size slide

  50. Airtestを利用して、高速連打し続ける
    30分間攻撃し続けて、バージョン毎の攻撃数に差異がないか?
    51

    View full-size slide

  51. CSVで行動を作れるようにする。 
     特定ルールでのスキル効果チェック
    52

    View full-size slide

  52. CSVを利用したテストの作成
    53
    デバッグ用APIで各種データ調整
    対戦相手の設定、ルール等
    ログをスプレットシートに記述
    端末を操作して自動実行させる
    API_XXX.py
    API_XXX.py

    View full-size slide

  53. 自動化のタスクを
    CSVで表現する
    task_classes = [TaskHttpGet,
    AttacklTask,
    TacticsTask,
    HealTask,
    SpecialTask,
    CheerTask,
    TaskBasicTap,
    TaskScreenShot,
    TaskHttpRuleChange,
    TaskHttpBattleBegin,
    TaskHttpBattleEnd,
    TaskHttpEditStatus,
    TaskStartWar,
    TaskGameStart,
    SpreadSheetLogger,
    TaskHttpEditHp,
    TaskTriggerCheck]
    obj_graph = pinject.new_object_graph(binding_specs=[PocoCSVBindingSpec()])
    for task in task_classes:
    instance = obj_graph.provide(task)
    self.worker[instance.tag()] = instance
    for index, row in df.iterrows():
    if row[0] in self.worker.keys():
    var = self.worker[row[0]].action(row)
    大まかに表現するとこのような
    感じ
    タスク毎にクラスを作成し、
    CSVのコード毎に呼び出す
    54

    View full-size slide

  54. CSVを利用したテストの作成
    例えば:
    ・テスト環境を構築するAPI
    ・「合戦」中に掛かる、倍率のリセット
    ・実際に端末を操作してテスト内容を実行するコマンド
    端末操作とAPI操作を1つのファイルから命令できるので
    手順ミス等がなくなる
    55

    View full-size slide

  55. Tips おまけ等
    56

    View full-size slide

  56. DeviceFarmerのユーザー認証
    CyberAgentでは「同一ネットワークのみ&LDAP認証」で運用
    https://github.com/openstf/stf/issues/47
    LDAP認証ってどうやるの?
    https://blog.cybozu.io/entry/2018/12/20/110000
    サイボウズさんの事例
    57

    View full-size slide

  57. 高速化のちょっと
    したコツ
    poco("UI名").click()
    が微妙に遅い…
    内部の「touch_method」
    によって手法が変わる
    #Before
    poco = UnityPoco()
    poco("LeftBgSprite").click()
    58

    View full-size slide

  58. 高速化のちょっと
    したコツ
    dev = device()
    poco = UnityPoco()
    screen_width, screen_height =
    poco.get_screen_size()
    pos_attack_scale_x, pos_attack_scale_y =
    poco("LeftBgSprite").get_position()
    pos_x = screen_width * pos_attack_scale_x
    dev.minitouch.touch((pos_x, pos_y))
    https://github.com/DeviceFarmer/minitouch
    59

    View full-size slide

  59. 起動時にUSBで接続済みのデバイスに
    モジュールがインストールされる
    接続しているAndroid端末に必要なモジュールがインストールされる
     minicap デバイスキャプチャツール
    ・https://github.com/openstf/minicap
      ・スクリーンショットの高速なやり取りに利用する
     minitouch android内のinputファイルを書き換えるツール
    ・https://github.com/DeviceFarmer/minitouch
      ・高速にタッチする処理等に利用
    60

    View full-size slide

  60. minitouch,minicapは元々OpenSTFの
    プロジェクト
    個別に公開されていたものが、Airtestで利用されているOSSとして公開したも
    のが別のプロジェクトで利用されOSSを公開した会社でそのプロジェクトを利用
    する。という面白い構図。
    61

    View full-size slide

  61. DeviceFarmerのiOS周り
    issueは上がっていて、プルリクエスト用のプロジェクトも進んでいる。
    自分でXcode周りのビルドができるのであれば試すことはできそう。
    間に合えば資料アップロード時にここらへんの資料を追加します。
    62

    View full-size slide

  62. AirtestとiOS :ちょっとまだ非推奨
    (スピードが遅い…)
    https://airtest.doc.io.netease.com/en/tutorial/6_IOS_automated_t
    esting/ を参考に
    https://github.com/AirtestProject/iOS-Tagent
    ↑をXcodeでビルドして、端末にインストール
    Mac上でhomebrewを導入し、brew install usbmuxd
    iproxy 8100 8100で接続
    63

    View full-size slide

  63. Pocoを拡張する
    Pocoは、GameObject内の
    Componentを取得して内部の
    値を返すという動きをしている
    ライブラリを拡張すれば独自の
    Componentの特定の値を
    取ってこれる
    64

    View full-size slide

  64. ADBを「合わせる」
    ADBのバージョンをあわせないと、AirtestやCLIで実行する際にADBを
    kill-server start-serverを繰り返すことになるので対応したい
    PIPとAirtestの中のadbをあわせるのにリンクを作成しておくとよい
    65

    View full-size slide

  65. DeviceFarmerを自力で用意するのが大変
    https://www.headspin.io/
    headspinさんのような
    サービスを利用するのも
    一つの手段
    66

    View full-size slide

  66. まとめ
    1.実機による自動テストが可能になることで、安定稼働とチームの安心感向上
    に寄与することができる
    2.プロジェクト外からテストによる貢献ができるので開発リソースの柔軟な投入
    が可能プロジェクト側にも負担をかけない
    3.Pythonの知識が多少要求されるが、十分習得可能な範囲と考えられ、非プ
    ログラマーでも自動のテストが構築できる
    67

    View full-size slide

  67. まとめ
    アイディアと今後の開発でさらに有効活用できると考えておりますので、みなさ
    んと一緒にいろいろ考えていけたら良いなと思っております。
    68

    View full-size slide

  68. 参考文献や素材等
    かわいいフリー素材集 いらすとや
    4,750,000+ free and premium vector icons in SVG, PNG, CSH and AI format.
    Azusa 3 - 大体いい感じになる無料 Keynote・Googleスライドテンプレート
    【Unity】「Airtest IDE」を使用して Android のゲームで画像認識による UI の自動テストを試してみた
    Androidのリモート操作/管理ツール「OpenSTF」をDocker for Windowsで使う
    OpenSTF+Dockerで社内Android端末管理システムを Mac上に構築する
    https://airtest.netease.com/
    AirtestProject/Poco-SDK
    Device Farmer
    stfのiosサポート https://github.com/tmobile/stf_ios_support
    Welcome to Poco (ポコ) documentation! — poco 1.0 documentation
    69

    View full-size slide