12
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

グリー/ポケラボ品質管理Advent Calendar 2024

Day 15

Pocoを利用したUnity製アプリのテスト自動化

Last updated at Posted at 2024-12-14

はじめに

去年まではスプレッドシートを利用した自動化をメインにアドベントカレンダーの記事を書いていましたが、今年はPocoを利用したUnity製アプリのテスト自動化を業務で行ったためナレッジのまとめを記事にすることとしました。
この記事をきっかけとして少しでも多くの方がPocoに興味を持っていただければ幸いです。
PocoはAirtestのサブセットとなっているためまずはAirtestとPocoについてお話しします。

AirtestとPocoについて

Airtestの概要

Airtestは、NetEaseが提供するオープンソースの自動化テストツールです。
主に画像認識を利用して、画面上の操作を自動化します。特にモバイルゲームやアプリのUIテストに適しており、コードの知識が少なくても使いやすいのが特徴です。
WindowsやAndroid、iOS、Unityエディタなど、多くのプラットフォームをサポートしています。

Pocoの概要

PocoはAirtestを補完するツールで、アプリのUI要素(ボタン、テキストボックスなど)を直接検出・操作するために設計されています。
ゲームエンジン(UnityやCocos2d)や一般的なモバイルアプリでのテストを、画像認識よりも効率的かつ安定して行えます。
UIツリーに基づいて要素を操作するため、デザイン変更に強いです。

AirtestとPocoの違い

Airtestは画面上の画像認識をベースにテストを自動化します。解像度やデザインの変更に弱い反面、見た目が重要なゲームやアプリに適しています。
Pocoは、アプリの内部構造(UIツリー)を活用して要素を識別するため、より正確で高速に操作できます。ただし、アプリがカスタマイズしたUIやUIツリーを使用している場合、利用が制限されます。

AirtestとPocoのメリット・デメリット

Airtest

メリット
・クライアントに依存しないためテストの導入が比較的容易
・画像認識でUIの見た目でテスト実装が可能
・アプリの操作やテストログの出力など基本的な機能がAPIとして利用可能

デメリット
・デザイン変更に弱い、画像認識の処理が重い

Poco

メリット
・UIツリーを使用するためテスト対象の誤検知が無くテストの安定性が高い
・画像認識と比べて処理が早い

デメリット
・UIツリーを使用するためアプリへのSDKの組み込みが必要
・このため開発/QAアプリへの組み込みとリリース用アプリでの切り離しが必要になる
・UIツリーが使用できないカスタマイズされたUIを使用している場合には使用できない
・UIツリーでGameObjectを識別するため、表示崩れをテストする場合個別に実装が必要になる

Airtest・Pocoを利用するための準備

AirtestIDEのインストール

AirtestIDE

テストスクリプトの実装・実行が簡単に行えるツール。
主にテスト自動化を行う前段階でのアプリのテスト実装行うために使用しています。
pythonでテストスクリプトを記述することになります。

Airtestパッケージのインストール

前述の通りAirtestはpythonでテストスクリプトを記述します。
AirtestIDEではGUIを使用してスクリプトを実装、実行しますが、ターミナルやコマンドラインからテストスクリプトを実行することが可能です。
テスト自動化のパイプラインにスクリプトの実行を組み込むためにはpythonでAirtestパッケージを使用します。

pip install -U Airtest

pocouiのインストール

Airtestで使用される機能はpythonのパッケージで使用されるため、pipを使用してPocoのパッケージをインストールします。
Airtestで使用しているpython、もしくはAirtestIDEで使用するpythonを選択することも可能なので使用するpythonに合わせてインストールします。

pip install -U pocoui

PocoSDKの組み込み

PocoSDK
UnityのAsset以下に配置します。最近は配置する場所に関する制約はゆるくなったとのことですが、慣習に従って「Assets/Scripts/」へUnity3Dを追加します。
次にUnityで使用しているUIのタイプにより、ugui、ngui、failyguiの中から一つ選択します。(使用するもの以外は削除)
一般的にビルドは開発用とリリース用で分けることが多いですが、#if文を使用してPocoMangerを使用するかどうかを制御するのが比較的簡単です。
意図せず公開用のビルドにPocoが組み込まれてしまったりすると容易にUIツリーの取得や操作を自動化されてしまう恐れがあるので気をつけましょう。

Pocoを利用するまでの準備はここまでです。
続いて今回Pocoでのテスト実装の説明に使用するサンプルアプリの説明をします。

サンプルで使用するUnityアプリの説明

今回Pocoを利用したテストの実装例を説明する際に使用するサンプルで使用するアプリの内容です。

サンプルで使用するシーンは二つで、ログイン画面とログイン完了画面です。

ログイン画面ではユーザーIDとパスワードを入力しログインボタンをタップします。
ユーザーIDとパスワードが正しい組み合わせの場合、ログイン完了画面へ遷移します。
間違った組み合わせの場合、エラーメッセージが表示されます。

ログイン完了画面では戻るボタンをタップすることでログイン画面へ遷移します。

パッケージ名

パッケージ名 役割
com.example.login Androidで起動時などに使用

シーン名

シーン名 役割
LoginScene ユーザーIDとパスワードの確認を行う
CompleteLoginScene ログイン完了画面

ログイン画面

GameObject名 役割
UserIDInput TMP_InputField ユーザーIDを入力
PasswordInput TMP_InputField パスワードを入力
SubmitButton Button 入力された内容で決定
ErrorMessageText TextMesh Pro エラーメッセージを表示
UIManager GameObject LoginManager.csをアタッチする空のGameObject

ここではサンプルとして使用するため、正しいUserIDとPasswordをそれぞれLoginManagerクラスのvalidUserIdとvalidPasswordにstringでセットしています。
SubmitButtonのonClickにOnLoginButtonClickをセットして、SubmitButtonがクリックされたタイミングで入力されたUserIDとPasswordが予め設定しておいたvalidUserIdとvalidPasswordと一致するかを判別します。
一致する場合にはログイン完了画面へ、一致しない場合にはエラーメッセージを表示する実装になっています。
またDEVELOPMENT_BUILDが有効な場合PocoManagerをインスタンス化します。

LoginManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using UnityEngine.SceneManagement;
using TMPro;
using UnityEngine.UI;

#if DEVELOPMENT_BUILD
using Poco;
#endif

public class LoginManager : MonoBehaviour
{
    public TMP_InputField userIdField;
    public TMP_InputField passwordField;
    public GameObject errorMessage;
    public Button SubmitButton;

    private string validUserId = "test";
    private string validPassword = "test";

    void Start()
    {
#if DEVELOPMENT_BUILD
        new PocoManager();
        Debug.Log("PocoSDK Initialized (Development Build)");
#endif

        errorMessage= GameObject.Find("ErrorMessageText");
		errorMessage.SetActive(false);
		userIdField = GameObject.Find("UserIDInput").GetComponent<TMP_InputField>();
		passwordField = GameObject.Find("PasswordInput").GetComponent<TMP_InputField>();
		SubmitButton = GameObject.Find("SubmitButton").GetComponent<Button>();
		SubmitButton.onClick.AddListener(this.OnLoginButtonClick);
    }

    public void OnLoginButtonClick()
    {
        string enteredUserId = userIdField.text;
        string enteredPassword = passwordField.text;

        if (enteredUserId == validUserId && enteredPassword == validPassword)
        {
            SceneManager.LoadScene("CompleteLoginScene");
        }
        else
        {
            errorMessage.gameObject.SetActive(true);
        }
    }
}

ログイン完了画面

GameObject名 役割
ReturnLoginButton Button ログイン画面に戻る
UIManager GameObject CompleteLoginManager.csをアタッチする空のGameObject
CompleteLoginManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class CompleteLoginManager : MonoBehaviour
{
    public Button ReturnLoginButton;

    void Start()
    {
		ReturnLoginButton = GameObject.Find("ReturnLoginButton").GetComponent<Button>();
		ReturnLoginButton.onClick.AddListener(this.OnReturnLoginButtonClick);
    }

    public void OnReturnLoginButtonClick()
    {
		SceneManager.LoadScene("LoginScene");
    }
}

今回のテストの実装に関係がないため詳細は省きますが、ReturnLoginButtonをクリックされたときにOnReturnLoginButtonClickが呼び出され、ログイン画面に戻ります。

Pocoを利用したテスト実装例

ここでは先ほど説明したサンプルアプリを使って、スモークテストの実装の例をあげていきます。
スモークテストではアプリの起動からログイン画面でログインが完了するまでの操作を実装します。

アプリの起動
1. アプリを起動する
2. ログイン画面で使用する「UserIDInput」オブジェクトが存在すれば成功

ログイン画面
1. UserIDInputをクリック
2. ソフトウェアキーボードが表示されるのでusernameで指定される文字列を入力
3. PasswordInputをクリック
4. ソフトウェアキーボードが表示されるのでpasswordで指定される文字列を入力
5. SubmitButtonをクリック
6. ログイン完了画面で表示される「CompleteLoginText」オブジェクトが存在すれば成功

スモークテストの実装

poco_test.py
from airtest.core.api import *
from poco.drivers.unity3d import UnityPoco
import time
from airtest.core.settings import Settings
from airtest.report.report import simple_report

# デバイス接続
device_serial = "123456789"  # 接続したいデバイスのシリアルID(adb devicesなどで調べる)
connect_device(f"Android:///{device_serial}")

#アプリID (パッケージ名)
APP_PACKAGE = "com.example.login"

#ログファイルの名前を設定
Settings.LOG_FILE = "log.txt"

#ログ出力のディレクトリを設定
set_logdir(r'Log')

# Pocoインスタンス作成
poco = UnityPoco()

#アプリの起動テスト
def test_app_launch():
    start_app(APP_PACKAGE)
    time.sleep(3)  # アプリ起動待機

    # スプラッシュ画面確認
    assert poco("UserIDInput").exists(), "UserIDInput not found!"
    print("App launched successfully!")

#ログインテスト
def test_login(username, password):
    poco("UserIDInput").click()
    text(username)

    poco("PasswordInput").click()
    text(password)

    time.sleep(3)  # 入力待機
    
    poco("SubmitButton").click()
    
    # ログイン成功確認
    assert poco("CompleteLoginText").exists(), "Login failed!"
    print("Login successful!")

# テストの実行
if __name__ == "__main__":
    try:
        # テストシナリオ実行
        test_app_launch() #アプリの起動
        test_login("test", "test") #ユーザー入力のUserIDとPassword
        print("Smoke test completed successfully!")
    except AssertionError as e:
        print(f"Test failed: {e}")

    #出力されたログをhtmlの形式で出力する
    simple_report(__file__,logfile=r"log.txt",output=r"html/log.html")

スモークテストの実行の準備

projectRoot
├─ Log
|  └─...
├─ html
│  └─... 
└─ poco_test.py

poco_test.pyをそのまま実行してもスクリプト内で参照するディレクトリが存在しないためエラーになります。
そのため上記のような構成になるように、予めLogディレクトリとhtmlディレクトリを作成しておきます。

スモークテストの実行

作成したスクリプトを実行します。

cd projectRoot
airtest run poco_test.py

テストのログとレポート

poco_test.pyの実装で、全てのテストが終了した後に出力されたログをhtmlとして出力し直す処理が実装されています。
出力先は「projectRoot/html/log.html」です。
そのままダブルクリックで開くとブラウザでテストレポートが表示されます。
またWebServerを設置して出力したhtmlへのURLを共有することで閲覧することも可能になります。

スクリーンショット 2024-12-13 0.57.19.png

スクリーンショット 2024-12-13 0.57.48.png

まとめ

今回は

  • AirtestとPocoについての概要
  • AirtestとPocoのメリットとデメリット
  • AirtestとPocoを利用するための準備
  • サンプルアプリ
  • スモークテストの実装・実行
  • テストのログ・レポート

という内容で、ざっくりとUnity製のアプリでの実装内容とPocoを使ってどのようにスモークテストの自動化を行うかについて一通りサンプルを交えながら解説しました。

これまでは画像認識でのテスト自動化を行ってきましたが、Pocoを利用することでUIツリーを利用したテスト自動化も可能になりました。
双方にメリットとデメリットがあるためどちらかと選択できるものではなく相互にメリットを活かし、デメリットを減らすという考え方が今後必要になります。

Airtestのみを使う場合ではテスト対象を画像で識別できないことにによりテストを継続することが困難になりますが、Pocoを併用することにより画像認識で失敗したとしもテスト結果を残しつつUIベースでのテストが継続できるため、網羅的なテストが可能となります。

Pocoを併用する場合、特にセットアップの複雑さや学習コストの面でAirtest単体での使用と比べてデメリットはありますが、動的な要素への対応や速度、メンテナンス性におけるメリットは大きいため、デメリットを減らすためのナレッジとどのようにしてナレッジを横展開していくかが今後Pocoの導入を広めていくカギになると考えています。

今回の記事が自動化を進めたいと思っている方の一助になれば幸いです。

おわり

12
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?