1. テストパッケージを追加する
Gua.Testing.UnityはNuGetで公開されています。次のコマンドは最新の安定版を解決します。projectへ書き込まれたversionをcommitし、3つのGuaパッケージを同じversion系列へ揃えてください。
dotnet add package Gua.Core
dotnet add package Gua.Testing
dotnet add package Gua.Testing.Unityパッケージごとの役割
| コマンド | 追加される機能 |
|---|---|
dotnet add package Gua.Core | Guaの共通型とnative runtime境界を追加します。 |
dotnet add package Gua.Testing | Godot版と共通のSemantic locator、action、待機、assertion、diagnosticsを追加します。 |
dotnet add package Gua.Testing.Unity | Unity Editor/Playerのbuild・起動・bridge接続・終了処理を行うhostを追加します。 |
2. PlayerまたはEditorを起動する
using Gua.Core;
using Gua.Testing;
using Gua.Testing.Unity;
var player = UnityPlayerBuilder.Build(
@"game/Assets/Scenes/Title.unity");
using var host = UnitySceneTestHost.LoadRenderedPlayer(
player, UnitySceneTestHostOptions.StrictIsolation);
GuaAssertions.GetByRole(
host.Context, "button", "Start Game"
).ToBeVisible();
await GuaAssertions.GetById(
host.Context, "start"
).ClickAsync();
await GuaAssertions.WaitForTextAsync(
host.Context, "loading", "Loading..."
);
var screenshot = host.CaptureScreenshot();Unityテストを処理順に読む
| 記述 | 意味 |
|---|---|
using Gua.Core; | reset policyなど共通runtime型を読み込みます。 |
using Gua.Testing; | Semantic locator、action、待機、assertionを読み込みます。 |
using Gua.Testing.Unity; | Unity Player builderとscene test hostを読み込みます。 |
UnityPlayerBuilder.Build("...Title.unity") | Unity Editorをbatchmodeで起動し、指定sceneを含むWindows x64 Mono Playerを一時directoryへbuildします。 |
LoadRenderedPlayer(player, StrictIsolation) | 描画付きPlayerを起動してbridgeへ接続し、開始・終了時の状態漏れを厳格に検査します。 |
GetByRole(..., "button", "Start Game") | UI Toolkit/uGUI/TMPの違いを意識せず、roleと名前でButtonを検索します。 |
.ToBeVisible() | 現在のSemantic snapshotでButtonが表示されていることを確認します。 |
GetById(..., "start").ClickAsync() | 固定ID startへclickを送り、Unity listenerが同期処理を終えた結果まで待ちます。 |
WaitForTextAsync(..., "loading", "Loading...") | 新しいsnapshotを再取得し、画面側の結果が実際に公開されるまで待ちます。 |
host.CaptureScreenshot() | 現在frameより後の描画完了時にUnityへPNG取得を要求します。rendered hostが必要です。 |
using var host | scope終了時にbridgeを閉じ、設定に従ってUnity processを終了します。 |
| 入口 | 用途 |
|---|---|
LoadPlayer | -batchmode -nographicsの既存Player。高速なsemanticテスト向け |
LoadRenderedPlayer | 描画付き既存Player。screenshot/Visual比較向け |
LoadEditor | 指定sceneをEditor Play Modeで起動 |
BuildAndLoadPlayer | sceneからWindows x64 Mono Playerをビルドして起動 |
3. Godot版と同じSemantic APIで操作する
host.Contextはengine共通のIGuaContextです。Unity固有APIでButtonを探すのではなく、Godot版と同じlocatorと状態待機を利用します。
var name = GuaAssertions.Query(host.Context)
.ByRole("textbox").Within("LoginForm").Get();
await name.SetValueAsync("alice");
await name.FocusAsync();
await GuaAssertions.PressKeyAsync(host.Context, "Enter");
await GuaAssertions.GetById(host.Context, "RememberMe")
.SetCheckedAsync(true);
await name.WaitForValueAsync("alice");各Semantic操作の意味
| 記述 | 意味 |
|---|---|
Query(host.Context).ByRole("textbox") | Unity UIをtextboxという共通roleで検索します。 |
.Within("LoginForm").Get() | LoginForm配下に範囲を絞り、ちょうど1件の一致を要求します。 |
SetValueAsync("alice") | TextField、uGUI InputField、TMP_InputFieldのいずれでも共通actionとして値を設定します。 |
FocusAsync() | 対象をUnityのfocus経路へ設定し、続くglobal key入力の宛先にします。 |
PressKeyAsync(..., "Enter") | focus中のtextboxへkey-down/key-up gestureを送ります。 |
SetCheckedAsync(true) | UI ToolkitまたはuGUI/TMPのcheckbox/toggleをchecked状態にします。 |
WaitForValueAsync("alice") | action結果だけでなく、次の公開frameで値が観測されるまで待ちます。 |
安定したUnityテストにする4原則
Semantic testはheadless、screenshot/Visualはrendered PlayerまたはEditorを使います。
Unityのframe時間を固定sleepで推測せず、期待するnode・text・valueを待ちます。
空きportとStrict resetでprocess間の衝突や未消費requestを検出します。
UI Tree、操作履歴、Unity log、process情報、必要ならPNGを保存します。
Visual比較と操作記録
Gua.Testing.VisualとGua.Testing.Recordingはengine共通パッケージなので、Gua.Testing.Unityのhost.Contextでも利用できます。Visual比較にはPNGを取得できるLoadRenderedPlayerまたはEditor Play Modeを使います。Recordingはsemantic actionを扱うためheadless Playerでも利用できます。
using Gua.Testing.Recording;
using Gua.Testing.Visual;
var comparison = await GuaVisualAssertions.ExpectScreenshotAsync(
host.Context, "title-screen", new() {
BaselineDirectory = "baselines",
ArtifactDirectory = "artifacts/gua",
});
var recorder = new GuaRecorder(host.Context);
await recorder.ClickAsync(new(Id: "start"));VisualとRecordingの記述
| 記述 | 意味 |
|---|---|
using Gua.Testing.Visual; | engine非依存のPNG baseline比較APIを読み込みます。 |
ExpectScreenshotAsync(host.Context, "title-screen", ...) | UnityへPNGを要求し、title-screenというtest名のreview済みbaselineと比較します。 |
BaselineDirectory = "baselines" | 期待画像をversion管理するdirectoryです。通常実行では暗黙更新しません。 |
ArtifactDirectory = "artifacts/gua" | 不一致時のactual、expected、diff、manifestを保存します。 |
new GuaRecorder(host.Context) | 同じUnity contextへ送るSemantic操作をrecordingへ記録します。 |
recorder.ClickAsync(new(Id: "start")) | startへのclickを実行し、request-correlated完了を待って再生可能なstepとして残します。 |
並列実行と分離
UseAvailableBridgePortは既定でtrueです。ホストがloopbackの空きポートを予約し、子プロセスへGUA_BRIDGE_PORTを渡すため、固定8765の衝突を避けられます。
ただし番号を選んだ直後にlistenerを閉じてからUnityを起動するため、bind完了までを予約する仕組みではありません。極端な並列負荷では小さな競合余地が残ります。
var options = new UnitySceneTestHostOptions {
UnityExecutablePath = @"C:/Program Files/Unity/Hub/Editor/6000.5.3f1/Editor/Unity.exe",
ProjectPath = @"C:/src/game",
UseAvailableBridgePort = true,
ConnectTimeout = TimeSpan.FromSeconds(60),
TeardownResetPolicy = GuaResetPolicy.Strict,
CaptureDiagnosticsBeforeTeardown = true,
};Unity host optionの意味
| option | 意味 |
|---|---|
UnityExecutablePath | 使用するUnity.exeを明示します。省略時は環境変数とHub installを探索します。 |
ProjectPath | AssetsとProjectSettingsを含むUnity project rootです。 |
UseAvailableBridgePort = true | loopbackの空きportを選び、GUA_BRIDGE_PORTとして子processへ渡します。 |
ConnectTimeout = 60秒 | Unity起動後、bridgeへ接続できるまで待つ上限です。 |
TeardownResetPolicy = Strict | 終了時の未消費request/eventを黙って捨てず失敗として報告します。 |
CaptureDiagnosticsBeforeTeardown = true | teardown失敗時、Unity processを終了する前に証拠を取得します。 |
診断とスクリーンショット
CreateDiagnosticsSession()はUI Tree、操作履歴、runtime diagnosticsに加え、-logFileで得たUnity log、PID、bridge URL、log pathをartifactへ含めます。stdout/stderrを別々にはredirectしません。必要ならcaptureScreenshot: trueを指定します。Strict teardownは未消費request/event等を黙って消さず失敗として報告します。
using var diagnostics = host.CreateDiagnosticsSession(
TestContext.CurrentContext.Test.FullName,
outputDirectory: "artifacts/gua",
captureScreenshot: true);
using var assertionScope = GuaAssertionScope.Use(
new GuaAssertionOptions {
DiagnosticsSession = diagnostics,
});
GuaAssertions.GetByRole(
host.Context, "button", "Start Game"
).ToBeVisible();診断設定の意味
| 記述 | 意味 |
|---|---|
host.CreateDiagnosticsSession(...) | 共通Gua diagnosticsへUnity process固有のlogとmetadataを加えるsessionを作ります。 |
TestContext...Test.FullName | NUnitのtest名をartifact識別子にします。 |
outputDirectory: "artifacts/gua" | 失敗証拠の保存先を指定します。 |
captureScreenshot: true | semantic情報に加えてon-demand PNGも取得します。headless hostでは利用できません。 |
GuaAssertionScope.Use(...) | scope内のGua assertion失敗とdiagnostics sessionを関連付けます。 |
.ToBeVisible() | 失敗時は元のassertionを主例外として保ったまま証拠を保存します。 |
SceneTimeoutは現状、主にscreenshot待機の既定値です。Scene遷移を自動で待つAPIではありません。