Hello,
I’m working on a project using the new Input System and I’d like to write test to assert specific behaviors. For that I need to simulate user input in order to trigger specific actions but I have no clue how to do it.
Is it possible ? How would you do it ?
Thx
You’d need a robot to press buttons on a physical device such as a gamepad.
But I think I get what you mean, and that is actually pretty easy. Assuming you have an input action map like this one:
You will likely have a class like the following which receives the input events and either registers them in a “current state” (as in this example) or it will call some other methods, preferably just calling a delegate Action or event.
[RequireComponent(typeof(PlayerInput))]
public sealed class FirstPersonInputReceiver : MonoBehaviour
{
[Serializable]
public struct InputState
{
public Vector2 MoveDir;
public Vector2 LookDir;
public bool JumpPressed;
public bool AttackPressed;
public Vector3 GetMoveDir() => new(MoveDir.x, 0f, MoveDir.y);
public Vector3 GetLookDir() => new(LookDir.x, 0f, LookDir.y);
}
[SerializeField] private InputState _currentState;
public bool CancelPressed { get; private set; }
public InputState CurrentState => _currentState;
public void OnMove(InputValue value) => _currentState.MoveDir = value.Get<Vector2>();
public void OnLook(InputValue value) => _currentState.LookDir = value.Get<Vector2>();
public void OnJump(InputValue value) => _currentState.JumpPressed = value.isPressed;
public void OnAttack(InputValue value) => _currentState.AttackPressed = value.isPressed;
public void OnCancel(InputValue value) => CancelPressed = value.isPressed;
}
What you need to do is to either make that class have an interface, which just contains the InputState CurrentState property such that you can write a mock class for tests that also implements the interface and artificially generates a state that your tests run against.
Similarly, if this input receiver class is sending out message, events or calls delegates, you’d abstract those into an interface and test against the interface (and generally code against the interface too), where the tests will work with a mock class that generates input events.
Meaning between where you receive New Input System events like OnMove(InputValue value) and the processing (use) of the move value you simply code against an interface like IInputProvider which has either the above CurrentState property (polling input) or individual event methods like OnMoveEvent(Vector2 dir) aka event-driven input.
Btw, you could even setup a input receiver class that records the input events and serializes them. With the polling approach using InputState you’d only need a List either one per frame or you add some timing info to it (ie frame number) so you now when to change the state.
The same approach can be used to record and playback game demos, both for testing and entertainment (think: Doom or Quake self-running demo loops).
Hello,
Thanks for the answer, I think I get it !