先日リリースされたUnity 6.3から、UI Toolkit(旧UIElements)を使ったUIのテストをサポートするUI Test Frameworkパッケージ(com.unity.ui.test-framework)が使えるようになりました。
UI Toolkitは徐々に存在感を増しながらも、テスタビリティがとても低いという致命的な問題がありました。 UI Test Frameworkパッケージはまだ完全とは言えなませんが、ランタイムもUI Toolkitで作るという話が少し現実味を帯びたように思います*1。
本記事は、少しさわってみた範囲のメモ書きです。
前提:UI Toolkitの何が問題なのか
UI ToolkitはuGUIと異なり、GameObjectと紐づくのはルート要素であるUIDocumentコンポーネントだけであり、配下のUI要素は別空間です。
EventSystemやInputModuleでは干渉できません。
従って、UI要素を操作するテストを書くにはUI ToolkitのAPIに頼らざるを得ません。
そのAPI、UI要素の検索はQuery<T>で可能ですが、クリックなどのイベントを送るSendEventはテストコードから呼んでも動作しません*2。
ほか、ListViewのchildCountやselectedItemが取得できないといった問題もあります。
環境
- Unity 6000.3.0b1
- UI Test Frameworkパッケージ(com.unity.ui.test-framework)v1.0.0
UI Test Frameworkパッケージのインストール
UI Test Frameworkパッケージは、Unity 6.3以降でインストールできます。 Package Managerウィンドウを開き、Unity Registryから検索してインストールします。

UI Test Frameworkパッケージの概要
UI Test Frameworkパッケージには、
UnityEditor.UIElements.TestFrameworkとUnityEngine.UIElements.TestFrameworkの2つの名前空間があります。
基本的なAPIは共通のベースクラスに実装されており、エディタ側にはコンテキストメニューやインスペクターウィンドウのサポートがあります。
テストクラスは、EditorUITestFixtureもしくはRuntimeUITestFixtureを継承*3することでPanelSimulatorなどシミュレータ系のAPIを使ったテストを書くことができます。
ボタンをクリックするテストの例
[TestFixture] public class ButtonTest : RuntimeUITestFixture // 継承 { [Test] [LoadScene("../../../Scenes/Button.unity")] public async Task ボタンをクリック_ラベルの数字がインクリメントすること() { // Arrange var uiDocument = Object.FindAnyObjectByType<UIDocument>(); var root = uiDocument.rootVisualElement; var button = root.Query<Button>("increment_button").First(); var label = root.Query<Label>("counter_text").First(); Assume.That(label.text, Is.EqualTo("0")); // ラベルの初期値は "0" // Act simulate.Click(button); // Assert Assert.That(label.text, Is.EqualTo("1")); // ボタンクリックでインクリメントされることを検証 } }
Arrangeでは、Sceneファイルをロードし、GameObjectに紐づいたUIDocumentを取得、配下のボタンとラベルを取得しています。
ここまでは通常のUI ToolkitのAPIです。
Actで使用しているsimulateはRuntimeUITestFixtureのプロパティで、型はPanelSimulatorです。
Clickメソッドを使ってbuttonのクリック操作を行います。
PanelSimulatorには、クリックするスクリーン座標を渡すClickのオーバーロードがあるほか、DragAndDropやKeyDownなどのメソッドが実装されています。
なお、Clickは同期メソッドになっていますが、テストメソッドは非同期(async TestもしくはUnityTest属性)でないと動作しません。
UITestFrameworkRuntimeOptionsアセット
RuntimeUITestFixtureのテストを実行するには、UITestFrameworkRuntimeOptionsアセットファイルが必要です。
このファイルは、コンテキストメニューから Create > UI Toolkit > UI Tests Runtime Optionsで生成できます。これをResourcesフォルダ下に置きます。
UITestFixtureにrootVisualElementをセットする
正確には、RuntimeUITestFixtureが保持するPanelSimulatorが保持するrootVisualElementです。
TestFixture(テストクラス)がひとつのrootVisualElementからなるUI要素のツリーを保持するようになっています。
前述のClickメソッドなどはrootVisualElement未設定でも動作しますが、FrameUpdateメソッド系を使用するとき例外が出ます。
rootVisualElementは直接設定するのではなく、SetUIContentメソッドにUIDocumentを渡して設定します。
先のコードのArrangeフェーズを書き換えると次のようになります。
var uiDocument = Object.FindAnyObjectByType<UIDocument>(); SetUIContent(uiDocument); var button = rootVisualElement.Query<Button>("increment_button").First(); var label = rootVisualElement.Query<Label>("counter_text").First();
所感
少し触っただけですが、クリックなどのイベントが動作するだけでも大きく前進したと言えます。 またランタイムでなくエディタに目を向けると、IMGUIはほぼテスト不能でしたのでUI Toolkitに乗り換えも検討しようかという気持ちになりました。
未検証ですが、MenuSimulator、PopupMenuSimulator、ContextMenuSimulator、InspectorTestUtilityといったクラスは気になっています。
ただ全体に、ユニットテスト向けという印象を受けました。UIに対するユニットテストはROIが低くなるので避けたほうがよく、UI操作は画面遷移を伴う統合テストで使いたいのですが。