Edited at
gumiDay 17

UnityでuGUIのUIテストを自動化

これはUnity 4.6から導入された新UI、uGUIでのUIテストの自動化方法の紹介です。


前提

チームで開発をしていると困るのが動かなくなるコミット。

「何もしていないのに急に動かなくなった」「git pullしたらボタン遷移しなくなった」経験ありませんか?困りますよね?

そこでUIテストを自動化すればそのような悩みを少し解消できますよ!


UIテスト自動化の仕組み

正常に動く時に「タップ操作を記録」して、随時「タップ操作の再生」を行い「テストの結果判定」で問題があれば通知する、という方法でやってみます。


タップ操作の記録

単純に毎フレームタップイベントがないか判断します。1

次にuGUIの「EventSystem.current.currentSelectedGameObject」を参照し、タップされたGameObjectを取得できます。

これでXY座標とGameObjectが手に入るので、タップ操作の記録ができました。

合わせてSystem.Diagnostics.Stopwatchを使ってタップ間隔も保持しておくと再生時に役立ちます。

IEnumerator record()

{
var timer = System.Diagnostics.Stopwatch.StartNew();
while (true)
{
yield return null;
if (!Input.GetMouseButtonDown(0))
{
continue;
}

var go = EventSystem.current.currentSelectedGameObject;
if (go == null)
{
Debug.Log("クリックされたけどUI部品は見つからなかった");
continue;
}

var p = Input.mousePosition;
Debug.Log(string.Format("クリック座標={0}x{1} クリックされたGameObject={2} 経過時間={3}",
p.x,
p.y,
go.name,
timer.Elapsed.TotalSeconds));
}
}


タップ操作の再生

再生の方はuGUIのEventSystemを色々と使います。

まずPointerEventDataでイベントインスタンスを作って、そこにタップ座標などの情報を設定し、EventSystem.current.RaycastAll()で当たり判定を行い、第二引数のresultsにヒットしたUI要素の一覧が格納されます。

あとはresultsの各要素に対してExecuteEvents.Execute()でイベントを発行すればOK。

以下の例では指定したXY座標にあるuGUIのButtonコンポーネントのOnClick()が実行されるようイベントを送信します。1

void play(int x, int y)

{
var ev = new PointerEventData(EventSystem.current);
var results = new List<RaycastResult>();
ev.position = new Vector2(x, y);
ev.button = PointerEventData.InputButton.Left;
EventSystem.current.RaycastAll(ev, results);
foreach (var result in results)
{
ExecuteEvents.Execute<IPointerClickHandler>(
result.gameObject,
ev,
(handler, eventData) => handler.OnPointerClick((PointerEventData)eventData));
}
}


テストの結果判定

EventSystem.current.RaycastAllで取得できる要素にGameObjectがありますので、このGameObjectが期待したものであればテストは成功、そうでなければテスト失敗としてもいいです。

さらにタップイベント送信時にApplication.CaptureScreenshotを使えば、自動でどんどんタップさせながら画面キャプチャを保存できテスト内容が視覚的に分かりやすくなります。画面キャプチャはテスト失敗時にも役立つでしょう。


まとめ

非常に短いコードでタップの記録と再生が実装できました。

uGUIは本当に便利ですね![^2]





  1. 動作確認しやすいよう左クリックでの判定でコードを載せましたがiPhoneとかで動作させるにはInput.Touchesを参照します