1. 必須NuGetパッケージを入れる
Godotの外部テストにはGua.Core、Gua.Testing、Gua.Testing.Godotの3パッケージが必要です。テストプロジェクトのディレクトリで次を実行します。現在のRelease系列を明示する場合は--version 0.12.0を付けます。
dotnet add package Gua.Core --version 0.12.0
dotnet add package Gua.Testing --version 0.12.0
dotnet add package Gua.Testing.Godot --version 0.12.0
# NUnitを使う場合
dotnet add package Microsoft.NET.Test.Sdk
dotnet add package NUnit
dotnet add package NUnit3TestAdapterGodotがPATHにない場合は環境変数を設定します。
$env:GODOT_EXECUTABLE = "C:\path\to\Godot_v4.7-stable_mono_win64_console.exe"
dotnet test YourGame.Tests.csproj2. シーンを起動して意味で操作
using Gua.Testing;
using Gua.Testing.Godot;
using var host = GodotSceneTestHost.Load(
"res://Main.tscn",
new GodotSceneTestHostOptions {
ProjectPath = projectPath,
UseAvailableBridgePort = true,
StartupResetPolicy = GuaResetPolicy.Strict,
TeardownResetPolicy = GuaResetPolicy.Strict,
});
GuaAssertions.GetByRole(
host.Context, "button", "Start Game"
).ToBeVisible();
await GuaAssertions.GetById(
host.Context, "start"
).ClickAsync();
await GuaAssertions.WaitForTextAsync(
host.Context, "loading", "Loading..."
);3. フォーム操作
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");安定したテストにする4原則
UseAvailableBridgePort = trueで並列テストの衝突を避けます。
開始・終了時のStrict resetで未消費要求やイベント漏れを検出します。
固定sleepではなく、状態・値・安定スナップショットを待ちます。
UI Tree、ログ、stdout/stderr、任意のPNGを診断成果物へ残します。
1. テストごとに空きポートを確保する
固定の8765を共有すると、並列実行時に一方のGodotだけがポートを取得します。ホストに空きポートを予約させ、子プロセスへGUA_BRIDGE_PORTとして渡します。
using var host = GodotSceneTestHost.Load(
"res://Main.tscn",
new GodotSceneTestHostOptions
{
ProjectPath = projectPath,
UseAvailableBridgePort = true,
});
// 予約されたURLは子プロセスとRemoteContextへ自動設定されます。2. 開始前と終了前にStrict resetを行う
前のテストが残したノード・要求・イベントを次のテストへ持ち込みません。終了時に未消費の要求やイベントがあれば、黙って削除せずテストを失敗させます。
using var host = GodotSceneTestHost.Load(
"res://Main.tscn",
new GodotSceneTestHostOptions
{
ProjectPath = projectPath,
UseAvailableBridgePort = true,
StartupResetPolicy = GuaResetPolicy.Strict,
TeardownResetPolicy = GuaResetPolicy.Strict,
CaptureDiagnosticsBeforeTeardown = true,
CleanupAfterLeakReport = true,
});3. Sleepではなく観測したい状態を待つ
Task.Delay(1000)では、速い環境で時間を無駄にし、遅い環境では不足します。操作後に期待するノード状態を直接待ちます。
await GuaAssertions.GetById(host.Context, "start")
.ClickAsync();
// 表示されるまで最新スナップショットを再取得します。
await GuaAssertions.WaitForVisibleAsync(
host.Context,
"loading",
timeout: TimeSpan.FromSeconds(3),
pollInterval: TimeSpan.FromMilliseconds(20));
// 描画後の状態が3フレーム変化しないことも確認できます。
await GuaAssertions.WaitForStableSnapshotAsync(
host.Context,
stableFrames: 3);4. アサーション失敗時の診断成果物を設定する
失敗時に最終UI Tree、操作履歴、ログ、Godotのstdout/stderrをartifacts/guaへ保存します。CIではこのディレクトリをテスト成果物としてアップロードします。
using var diagnostics = host.CreateDiagnosticsSession(
TestContext.CurrentContext.Test.FullName,
outputDirectory: Path.Combine(
TestContext.CurrentContext.WorkDirectory,
"artifacts", "gua"));
using var assertionScope = GuaAssertionScope.Use(
new GuaAssertionOptions
{
DiagnosticsSession = diagnostics,
});
// 失敗すると、主例外を保ったまま診断ファイルが保存されます。
GuaAssertions.GetByRole(
host.Context, "button", "Start Game"
).ToBeVisible();GitHub Actionsを使う場合は、テスト後にartifacts/gua/**をactions/upload-artifactへ渡すと、失敗した実行から診断ファイルを取得できます。
GitHub Actionsで実行する
公開Actionのlink1345/gua-testerを使うと、Windows runnerへのGodot導入、最新の配布済みGuaアドオンの配置、GODOT_EXECUTABLEの設定、dotnet testの実行をまとめて行えます。
name: Godot Gua UI tests
on:
pull_request:
push:
branches: [main]
jobs:
test:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Run Gua UI tests
uses: link1345/gua-tester@v1.2
with:
project-path: game
test-project: tests/GuaTester.Tests.csproj
godot-version: "4.7"
godot-status: stable