1
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?

【Unity6.3】UIToolkitをテストできるUI Test Frameworkが追加されたよ!

Posted at

はじめに

Unity 6.3で、UIToolkitをテストできる「UI Test Framework」が追加された
もともとUnityにはNUnitベースの「Unity Test Framework」というツールがあった
今回のアップデートで、それとは別軸でテスト用のパッケージが追加された形になる

UI Test Frameworkは、その名の通りUIをテストするためのフレームワークで、UIのテスト時に発生する煩わしいことを回避することができる
「デバイスの違いによる解像度, DPI差分」「フォントシステム」「長押しなどの状態変化」「データバインディング時の反映待ち」「キーボードやマウスによるイベント発火」などなど、、、UIのテストがめんどくさい理由を挙げればキリがない
このフレームワークにはさまざまな機能が用意されており、ドキュメントをざっと読んだだけだが、すぐ実用できそうなぐらいには充実している
(リリースしたばっかりでしょうがないけど、ドキュメントがもっとあると嬉しいな〜:kissing_heart:)

今回の記事ではUI Test Frameworkの基本的な使い方を説明する

参考動画と参考プロジェクト

Unite2025の公演から 「UI testing made easy with the UI Toolkit Test Framework」

UIToolkitのサンプルプロジェクトであるBagelGame

ドキュメント

はじめの一歩

今回はBagelGameを元に進めるので、事前にクローンしてUnityで開いておくこと

テストの実行

プロジェクトにはすでにテストが実装されているので、先にテストの実行方法を説明する
Window/General/Test Runnerから、テスト用のパネルを開くことができる
スクリーンショット 2025-12-11 1.10.30.png

パネル右下のRun Allからテストを実行することができる
テスト項目を選択した状態で、Run Selectedを押下することで特定のテストを実行することもできる
スクリーンショット 2025-12-11 1.11.44.png

基本的な使い方

大まかに以下のような使い方をする

AutoDetect_RuntimeExample.cs
// テスト対象のクラスではUITestFixtureを継承する
public class AutoDetect_RuntimeExample : UITestFixture
{
    // NUnitの属性. 各テストの実行前に呼ばれる
    [SetUp]
    public void SetUp()
    {
        VisualTreeAsset uxml = Resources.Load<VisualTreeAsset>("UITestFrameworkDocSample");

        // テスト対象となるVisualElementを, UITestFixtureから提供されるルートのVisualElementへくっつける
        uxml.CloneTree(rootVisualElement);

        // 反映のために、1ループ分のUIToolkitの処理を行う
        simulate.FrameUpdate();
    }

    // NUnitの属性. テストケースであることを示す
    [Test]
    public void EditorAndRuntimeExampleTest()
    {
        // ボタンが押されたフラグを設定
        var isActive = false;
        var button = rootVisualElement.Q<Button>();
        button.clicked += ()  => isActive = true;

        // ボタンを押す
        simulate.Click(button);

        // フラグがtrueじゃなかったらAssert
        Assert.IsTrue(isActive);
    }
}

基本的にコメントの通りだが、重要なのは「simulate.FrameUpdate();でUIToolkitの処理を進めること」と「simulate.Click(button);では、押下イベントを発火させているのではなく、ボタンの位置を実際にマウスでクリックすることをテストしているということ」の2点

特に後者について、コードを辿っていくと分かるのだが
以下のコードのようにボタンの位置でMouseDownとMouseUpをスケジュールしている
これによって、ボタンの前面に余計なUIがあって押せないみたいなことがないようテストが行える

Clickの奥
public static IEnumerator SimulateClick(this VisualElement ve, Vector2 position, MouseButton button = MouseButton.LeftMouse, EventModifiers modifiers = EventModifiers.None)
{
    yield return ve.DispatchAndWaitForNextFrame(new EventBase[]
    {
        EventHelpers.MakeMouseDownAt(position, button, modifiers, 1),
        EventHelpers.MakeMouseUpAt(position, button, modifiers, 1)
    });
}

テストの中身を見てみる

BagelGame内の MainMenuScreenTests.csを見てみる
コメントましましにしたコードを置いておく

MainMenuScreenTests.cs
using NUnit.Framework;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEngine.UIElements.TestFramework;

namespace Bagel.B03_MainMenu
{
    public class MainMenuScreenTests : UITestFixture
    {
        BagelTestAssetList m_BagelTestAssetList;

        // テスト前にVisualTreeAssetをロードしておく
        // テストケースが何パターンあろうが、ここは1回だけでいいのでOneTimeSetUpが使われている
        [OneTimeSetUp]
        public void OneTimeSetUp()
        {
            m_BagelTestAssetList = Resources.Load<BagelTestAssetList>("BagelTestAssetList");
        }

        // テストケースの実行後に呼ばれる
        // テストケースごとにrootVisualElementがClearされて欲しいので、OneTimeTearDownではなく、TearDownを
        [TearDown]
        public void TearDown()
        {
            rootVisualElement.Clear();
        }

        [Test]
        public void MainMenu()
        {
            // テストを行うためのrootVisualElementへテスト対象をくっつける
            m_BagelTestAssetList.mainMenuUxml.CloneTree(rootVisualElement);
            // 反映するためのFrameUpdate
            simulate.FrameUpdate();

            bool playClicked = false;
            bool exitClicked = false;

            // VisualElementを探してきて、テスト用のフラグセットを仕込む
            var mainMenuPaneManager = rootVisualElement.Q<MainMenuPaneManager>();
            mainMenuPaneManager.BindUI(new MainMenuPaneManager.Callbacks
            {
                onPlay = () => playClicked = true,
                onExit = () => exitClicked = true
            });

            // ボタンをクリックをスケジュール
            simulate.Click(mainMenuPaneManager.playButton);
            
            // ボタンクリックを実行
            simulate.FrameUpdate();

            // フラグチェック
            Assert.IsTrue(playClicked);

            // ボタンをクリックをスケジュール
            simulate.Click(mainMenuPaneManager.exitButton);
            
            // ボタンクリックを実行
            simulate.FrameUpdate();

            // フラグチェック
            Assert.IsTrue(exitClicked);
        }
    }
}

BagelGameでは、テスト内からテスト対象となるVisualTreeAssetを参照するために、まとめておくScriptableObjectを作っている
実際に自分でテストを実装するときも、同じようなものを作ると良さそう

基本的な使い方で説明した通りの書き方をしている

おわりに

UIToolkitは今後どんどん使っていこうと思っているので、かなり嬉しい機能だった
ただ、ダイアログ周りやアニメーションのあるUIはまだ結構辛そうなので今後に期待

1
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
1
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?