Jump to content
Unity Insider Forum

Wie kann ich Input in Unit Tests "faken"?


jtuchel
 Share

Recommended Posts

Hallo zusammen,

in meinem Projekt nutze ich das neue Input System und möchte eine Methode in einem Script testen, das einen Parameter vom Typ InputAction.CallbackContext übergeben bekommt.

Innerhalb der Methode lese ich dann von diesem Parameter den Input aus und reagiere dementsprechend.

 

Es ist möglich, diese Methode zu testen (den Output kann ich abfragen), aber dafür muss ich bei Aufruf diesen Parameter übergeben. Wie baue ich denn diesen Parameter mit Input zusammen?

 

Also sagen wir mal über den Input wird ein Vector2 aktualisiert und ich möchte testen, dass dieser Vector2.right ist, wie muss ich mir den Parameter zusammenbauen, damit die Methode den entsprechenden Wert ausliest und ich die beiden Vektoren vergleichen kann?

 

Vielen Dank schon einmal

Link to comment
Share on other sites

Das auszuwerten ist relativ einfach:

public void LeftRight(InputAction.CallbackContext context){
  links=false; //Testvariablen zum Auswerten in der Update oder so
  rechts=false; 
  var direction = context.ReadValue<Vector2>();
  
  if(direction[0] <0){ // [0] ist die X-Achse, [1] wäre die Y-Achse
    links=true;
  }
  if(direction[0] >0{
    rechts=true;
  }
}

Bei diesem Beispiel gehe ich davon aus, dass  das InputActionElement eine Komponente an deinem zu steuernden Gamobject ist.
Beim Bereich Behavior vom InputAction ist "Invoke Unity Events" eingestellt und im Bereich Events hast du dann für diese Action das entsprechende Script und Methode ausgewählt.

Link to comment
Share on other sites

Die Methode rufst du nich von deinem Testscript auf. Die wird nur vom Action Event aufgerufen.

Da sind doch die 2 Variablen, links und rechts. Wenn die im Script als Public deklariert sind, kannst du sie doch ständig vom Testscript aus abfragen.
Du kannst genauso auch einen Sprung rein in eine Methode von deinem Testscript erzeugen, wo du von mir aus den direction-Wert anstatt der Variablenzustände mit übergibst. Der direction-Wertist ein Float im Bereich von -1f bis 1f.

 

Link to comment
Share on other sites

Ich versuche mal kurz Beispielcode zu zeigen

public void Move(InputAction.CallbackContext inputContext)
{
    Vector2 newInput = inputContext.ReadValue<Vector2>();
    transform.position += new Vector3(newInput.x, newInput.y);
}

Dass das kein echter Code ist, ist hoffentlich klar :) Aber nehmen wir es mal an.

Dann könnte ich mit dem Testrunner einen Unit Test aufsetzen, indem ich mir ein leeres GameObject erzeuge, diesem GO das Script hinzufüge, das GO an eine bestimmte Stelle setze, die Move Methode aufrufe und im Test dann überprüfe, ob es sich danach an der erwarteten Position befindet.

Dazu muss ich mir aber eine Instanz von InputAction.CallbackContext zusammenbauen, die den zu lesenden Input enthält.

Und mir ist anhand der Docs

https://docs.unity3d.com/Packages/c....InputSystem.InputAction.CallbackContext.html

noch nicht klar, wie ich die erzeugte Instanz so manipuliere, dass die Zeile

Vector2 newInput = inputContext.ReadValue<Vector2>();

den Fake Input ausliest.

Link to comment
Share on other sites

@malzbie @jtuchel möchte einen Automation Test (keinen Unit Test!) schreiben, bei dem der Testcode die Spielfigur steuern kann. Damit kann man dann automatisiert testen, ob bestimmte Systeme funktionieren (wie "kann die Figur x Meter hoch springen"). Das Ziel ist hier, dass der Bewegungscode nicht extra erweitert werden muss, um diese "Kontrollübergabe" vom Spieler an den Testcode zu ermöglichen. Ob das Input-System das kann, weiß ich aber nicht.

Link to comment
Share on other sites

Genau, also ich habe das mal erweitert. Das Behaviour wäre

    public class MovementBehaviour : MonoBehaviour
    {
        public void Move(InputAction.CallbackContext inputContext)
        {
            Vector2 movementDirection = inputContext.ReadValue<Vector2>();
            transform.position += new Vector3(movementDirection.x, movementDirection.y, transform.position.z);
        }
    }

und der Test dazu wäre zb

    [TestFixture]
    public class MovementBehaviourTests
    {
        [Test]
        public void ItShouldMove()
        {
            GameObject gameObject = new GameObject();
            MovementBehaviour movementBehaviour = gameObject.AddComponent<MovementBehaviour>();
            
            // movementBehaviour.Move(); // pass in Vector2.right
            
            Assert.AreEqual(gameObject.transform.position, Vector3.right);
        }
    }

Mir ist halt nicht klar, wie ich im Test den Parameter richtig übergebe.

Link to comment
Share on other sites

Wie der Parameter richtig übergeben wird, kann ich dir auch nicht sagen. Das ist jedenfalls ein Json Ausdruck.

Aber vielleicht kriegst du das raus, denn du kannst, wenn du ein Input Action Asset erstellt hast, im Inspector bei dem Asset einen haken setzen.
Dadurch wird ein c# Script mit gleichem Namen wie dein Asset erzeugt.

485701136_hakenpng.png.eb6b33dd756d1c35ac5f2ad83865fabd.png
Wenn du dir das mal anschaust, kommst du vielleicht dahinter. Ich steige da nicht ganz so durch.
Jedenfalls kannst du auch mittels dieses Scripts eine andere Art der Inputabfrage erzeugen. Siehst du unten im Script.

Wie dem auch sei. Du willst ja eigentlich nur über ein Testscript Werte in deinen player rein bringen und so tun als ob du ein Input bekommen würdest.
So habe ich es jedenfalls verstanden.
Warum willst du dafür unbedingt in die Methode rein springen, die ein InputAction.CallbackContext erwartet.
Bau dir doch eine public Methode, die dein Vector2 aufnehmen kann und dann genauso die Bewegung des Players bedient.

So etwa:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

public class InputTester : MonoBehaviour
{
    GameControls controls;
    public Vector2 movementDirection; // falls du die Werte abfragen willst
    void Awake()
    {
        controls = new GameControls(); // input Kontrollen erstellen
        controls.Gameplay.XAxis.performed += ctx => MoveLR(ctx); 
        controls.Gameplay.XAxis.canceled += ctx => MoveLR(ctx);
    }
    private void OnEnable()
    {
        controls.Gameplay.Enable();
    }
    private void OnDisable()
    {
        controls.Gameplay.Disable();
    }
    private void Update()
    {
        transform.position += new Vector3(movementDirection.x, movementDirection.y, transform.position.z);
    }
    void MoveLR(InputAction.CallbackContext context) 
    {
        var direction = context.ReadValue<Vector2>();
        movementDirection = direction;
    }
    public void TesteLR(Vector2 direction) // hier springt der Tester rein
    {
        movementDirection = direction;
    } 
    
}

In der Update wird movementDirection für das setzen der Position genutzt.
Ob die Variable nun in der Methode beschrieben wurde, die vom Input angesprochen wird, oder von der Methode, die dein Tester anspricht. Ist doch eigentlich egal, oder?
Aber vielleicht habe ich es immer noch nicht richtig vertstanden...oder du willst/kannst das Playerscript nicht mit einer weiteren Methode bestücken.
Na ja. Vielleicht hilft's ja doch.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

×
×
  • Create New...