入力のテスト
Input System は、入力の自動テストを作成するためのビルトインサポートを備えています。プラットフォームのバックエンドや実際のハードウェアデバイスに依存することなく、コードからすべての入力を実行できます。作成された自動入力テストでは、生成された入力が、実際のプラットフォームコードでランタイムに生成された入力と同じものとして扱われます。
テストアセンブリの設定
Input System の自動化フレームワークを使用するテストアセンブリを設定するには、以下のステップに従います。
- プロジェクトの
Packages/manifest.jsonファイルで、testablesにcom.unity.inputsystemを加える必要があります。この設定は、パッケージに付属するテストコードをプロジェクトのテストビルドに組み込むために必要です。
例えば、dependenciesプロパティの後に、以下のように記述を加えます。}, "testables" : [ "com.unity.inputsystem" ] - 新しいアセンブリ定義を作成する (メニュー: Create > Assembly Definition) か、既に作成したテストアセンブリのアセンブリ定義に移動します。
nunit.framework.dll、UnityEngine.TestRunner、UnityEditor.TestRunnerへの参照 (How to create a new test assembly で説明) と、Input System のUnity.InputSystemおよびUnity.InputSystem.TestFrameworkへの参照を加えます。

テストフィクスチャの設定
テスト用に分離されたバージョンの Input System を作成するには、InputTestFixture を使用します。フィクスチャにより、デフォルト初期化バージョンの空の Input System がテストごとに設定され、テストが完了すると、Input System が元の状態に復元されます。デフォルト初期化バージョンには、ビルトインの登録 (レイアウトやプロセッサーなど) がすべて含まれていますが、Input Device は存在しません。
ノート:
InputTestFixtureでは、[InitializeOnLoad]や[RuntimeInitializeOnLoadMethod]などの Unity の起動コードからカスタム登録が実行されることはありません。テストに必要なレイアウトは、テスト設定の一部として手動で登録する必要があります。
フィクスチャは、独自のフィクスチャの基本クラスとして使用できます。
class MyTests : InputTestFixture
{
[Test]
public void CanPressButtonOnGamepad()
{
var gamepad = InputSystem.AddDevice<Gamepad>();
Press(gamepad.buttonSouth);
}
// 設定時および終了時にカスタムロジックを実行する必要がある場合は、
// InputTestFixture から継承されたメソッドをオーバーライドします。
// 重要: テストフィクスチャ内のメソッドで NUnit の [Setup] および [TearDown] 属性を使用すると、
// InputTestFixture から継承されたメソッドが *オーバーライド* されるため、
// 元のメソッドは実行されなくなります。以下に示すように
// メソッドをオーバーライドするか、InputTestFixture の Setup() および
// TearDown() メソッドを明示的に呼び出してください。
public override void Setup()
{
base.Setup();
// ここに設定コードを加えます。
}
public override void TearDown()
{
// ここに終了コードを加えます。
base.TearDown();
}
}
重要: これを実装する場合は、
[SetUp]メソッドと[TearDown]メソッドのいずれも追加 しない でください。追加すると、InputTestFixture内のメソッドが呼び出されなくなるため、テストフィクスチャの初期化と終了が正しく行われません。代わりに、InputTestFixtureから継承されたSetupメソッドやTearDownメソッドをオーバーライドしてください。
別の方法として、フィクスチャ内でインスタンス化することもできます。
[TestFixture]
class MyTestFixture
{
private InputTestFixture input = new InputTestFixture();
// ノート: このシナリオでは、Setup() と TearDown() を手動で呼び出す必要があります。
[SetUp]
void Setup()
{
input.Setup();
}
[TearDown]
void TearDown()
{
input.TearDown();
}
}
これは特に、PrebuiltSetup を使用して、ゲームテスト用の大規模な設定を作成する場合に役立ちます。
[PrebuildSetup("GameTestPrebuildSetup")]
public class GameTestFixture
{
public Game game { get; set; }
public InputTestFixture input { get; set; }
public Mouse mouse { get; set; }
public Keyboard keyboard { get; set; }
public Touchscreen touchscreen { get; set; }
public Gamepad gamepad { get; set; }
//...
}
# if UNITY_EDITOR
public class GameTestPrebuildSetup : IPrebuildSetup
{
public void Setup()
{
UnityEditor.EditorBuildSettings.scenes = new[]
{
new UnityEditor.EditorBuildSettingsScene("Assets/Scenes/Main.unity", true)
};
}
}
# endif
一般に、設定した入力関連のデータをクリーンアップする必要は ありません。これには、加えたデバイス、登録したレイアウト、変更した InputSettings、その他 InputSystem の状態に対するあらゆる変更が含まれます。InputTestFixture は、Input System の現在の状態を自動的に破棄し、テストの開始前の状態を復元します。
テストの作成
テストを作成するときは、InputSystem.AddDevice<T>() を使用して新しい Device を加えます。
[Test]
public void PlayerInput_CanInstantiatePlayer_WithSpecificControlScheme()
{
InputSystem.AddDevice<Gamepad>();
var keyboard = InputSystem.AddDevice<Keyboard>();
var mouse = InputSystem.AddDevice<Mouse>();
var prefab = new GameObject();
prefab.SetActive(false);
var prefabPlayerInput = prefab.AddComponent<PlayerInput>();
prefabPlayerInput.actions = InputActionAsset.FromJson(kActions);
var player = PlayerInput.Instantiate(prefab, controlScheme: "Keyboard&Mouse");
Assert.That(player.devices, Is.EquivalentTo(new InputDevice[] { keyboard, mouse }));
Assert.That(player.controlScheme, Is.EqualTo("Keyboard&Mouse"));
}
入力を生成する最も簡単な方法は、InputTestFixture によって提供される Press(button)、Release(button)、PressAndRelease(button)、Set(control,value)、Trigger(action) の各ヘルパーメソッドを使用することです。
[Test]
public void Actions_WhenDisabled_CancelAllStartedInteractions()
{
var gamepad = InputSystem.AddDevice<Gamepad>();
var action1 = new InputAction("action1", binding: "<Gamepad>/buttonSouth", interactions: "Hold");
var action2 = new InputAction("action2", binding: "<Gamepad>/leftStick");
action1.Enable();
action2.Enable();
Press(gamepad.buttonSouth);
Set(gamepad.leftStick, new Vector2(0.123f, 0.234f));
using (var trace = new InputActionTrace())
{
trace.SubscribeTo(action1);
trace.SubscribeTo(action2);
runtime.currentTime = 0.234f;
runtime.advanceTimeEachDynamicUpdate = 0;
action1.Disable();
action2.Disable();
var actions = trace.ToArray();
Assert.That(actions.Length, Is.EqualTo(2));
//...
}
}
または、コードを使用して任意の入力イベントをシステムに送信し、任意の入力更新を実行することができます。
[Test]
public void PlayerInput_JoiningPlayerThroughButtonPress_WillFailIfDeviceIsNotUsableWithPlayerActions()
{
var playerPrefab = new GameObject();
playerPrefab.SetActive(false);
playerPrefab.AddComponent<PlayerInput>();
playerPrefab.GetComponent<PlayerInput>().actions = InputActionAsset.FromJson(kActions);
var manager = new GameObject();
var listener = manager.AddComponent<MessageListener>();
var managerComponent = manager.AddComponent<PlayerInputManager>();
managerComponent.joinBehavior = PlayerJoinBehavior.JoinPlayersWhenButtonIsPressed;
managerComponent.playerPrefab = playerPrefab;
// 1 つのボタンコントロールを含む HID レイアウトに基づいて Device を作成します。
const string kLayout = @"
{
""name"" : ""TestDevice"",
""extend"" : ""HID"",
""controls"" : [
{ ""name"" : ""button"", ""layout"" : ""Button"" }
]
}
";
InputSystem.RegisterLayout(kLayout);
var device = InputSystem.AddDevice("TestDevice");
using (StateEvent.From(device, out var eventPtr))
{
((ButtonControl)device["button"]).WriteValueIntoEvent(1f, eventPtr);
InputSystem.QueueEvent(eventPtr);
InputSystem.Update();
}
Assert.That(listener.messages, Is.Empty);
Assert.That(PlayerInput.all, Is.Empty);
}
ノート: 参考として、GitHub リポジトリ を参照して、Input System 自体のテストを見つけることができます。