Jump to content
Unity Insider Forum
Bradley

Unit Testing von Monobehaivor Klassen

Recommended Posts

Ich will meinen Code etwas solider gestalten und versuche mich gerade in die Unittests mit Monobehaivour heran zu tasten.

Im Moment scheitere ich aber schon daran überhaupt mein Script aufgerufen zu bekommen:

        public void CheckTextInTooltip()
        {
            bool result = false;
            ToolTipText toolTip = gameObject.AddComponent(typeof(ToolTipText)) as ToolTipText;


            result = toolTip.ToolTipTextInputCheck("");
            Assert.IsFalse(result);
        }

 

Der Editor verkündet immer ein NullReference Error.

Dass ich solche Klassen nicht einfach mit New Instanziieren kann ist mir mittlerweile klar.

 

Eine weitere Frage:

Kann ich diese Unittests auch automatisiert in Azure DevOps testen lassen?

Share this post


Link to post
Share on other sites

Unity hat ein eigenes Test-Framework, das teilweise auf NUnit basiert. Schau dir dazu einfach mal die verfügbaren Tutorials an.

Warum da jetzt eine NullReferenceException kommt, ist schwer zu sagen. AddComponent sieht richtig aus (auch wenn die generische Version etwas schlanker ist). Fliegt die Exception denn in der "result = "-Zeile? Ansonsten könnte höchstens noch, je nach dem wie hier der Kontext ist, gameObject null sein.

Share this post


Link to post
Share on other sites

Tatsächlich fliegt der Fehler schon bei:

ToolTipText toolTip = gameObject.AddComponent(typeof(ToolTipText)) as ToolTipText;

Der genau Fehlertext:

Zitat

CheckTextInTooltip (0,001s)
---
System.NullReferenceException :
---
at (wrapper managed-to-native) UnityEngine.Component.get_gameObject(UnityEngine.Component)
  at Tests.ToolTipWindow_Test.CheckTextInTooltip () [0x00003] in C:\Users\xxxxxx\Documents\SpaceOperaUnity\Assets\SpaceOpera\Tests\ToolTipWindow_Test.cs:16 
  at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in <567df3e0919241ba98db88bec4c6696f>:0

 

 

Ich hänge mal die Entsprechende (noch schlampig aussehende) Klasse an.

 

using UnityEngine;
using UnityEngine.UI;

public class ToolTipText : MonoBehaviour
{
    [Header("Einstellungen")]

    [Tooltip("Zeit in Sekunden bis das Tooltip erscheint")]
    [Range(0.0f, 5.0f)]
    public float targetTime = 1.5f;

    [Tooltip("Gibt an ob das Tooltip per Default Aktiv ist")]
    public bool ToolTipAktiv = true;

    [Tooltip("Gibt an ob das Tooltip aktuell aktiv ist oder nicht")]
    public bool ToolTipVisible = false;


    [Header("Zuweisungen")]
    public Text toolTipText;
    public GameObject toolTipPanel;


    [SerializeField] private float deltaTimer = 0.0f;
    Vector3 MousCordOffset = new Vector3(0, 0, 0);

    private void Start()
    {
            
    }

    public void ToolTip(string Text)
    {
        toolTipText.text = Text;
    }


    public void Update()
    {
        if (ToolTipAktiv)
        {
            ToolTipVisible = ToolTipTextInputCheck(toolTipText.text);
            toolTipPanel.gameObject.transform.position = ShowToolTip(ToolTipVisible);
            Debug.Log(toolTipPanel.gameObject.transform.position);

            //if (!ToolTipVisible)
            //{
            //    deltaTimer -= Time.deltaTime;
            //    if (deltaTimer <= 0.0f)
            //    {
            //        ToolTipVisible = true;
            //    }
            //}
            //else
            //{

            //    MousCordOffset.x = Input.mousePosition.x + 20;
            //    MousCordOffset.y = Input.mousePosition.y + 00;

            //    toolTipPanel.gameObject.transform.position = MousCordOffset; 
            //}
        }
    }

    /// <summary>
    /// In abhängigkeit vom Text wird True oder False zurück gegeben
    /// </summary>
    /// <param name="text">Text kommt vom tooltip.text</param>
    /// <returns>Ist text leer, wird False zurück gegeben. Sonst True</returns>
    public bool ToolTipTextInputCheck(string text)
    {
        if (text.Length == 0 || text == null)
        {
            toolTipPanel.gameObject.transform.position = new Vector3(-500, -500, 0);
            deltaTimer = targetTime;

            return false;
        }
        return true;
    }
    private Vector3 ShowToolTip(bool toolTipVisible)
    {
        if (!toolTipVisible)
        {
            deltaTimer -= Time.deltaTime;
            if (deltaTimer <= 0.0f)
            {
                ToolTipVisible = true;
            }
            return new Vector3(-500, -500, 0);
        }
        else
        {

            MousCordOffset.x = Input.mousePosition.x + 20;
            MousCordOffset.y = Input.mousePosition.y + 00;

            return MousCordOffset;
        }
    }

}

 

Share this post


Link to post
Share on other sites

Viel interessanter wäre die Klasse ToolTipWindow_Test. Ich gehe einfach mal davon aus, dass das keine MonoBehaviour-Klasse ist und du daher die Variable "gameObject" selbst definiert hast. Stimmt das?

Share this post


Link to post
Share on other sites

Tatsächlich ist Sie eine.
 

using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;


namespace Tests
{
    
    public class ToolTipWindow_Test : MonoBehaviour
    {
        [Test]
        public void CheckTextInTooltip()
        {
            bool result = false;
            ToolTipText toolTip = gameObject.AddComponent(typeof(ToolTipText)) as ToolTipText;


            result = toolTip.ToolTipTextInputCheck("");
            Assert.IsFalse(result);
        }
    }
}

 

Share this post


Link to post
Share on other sites

Ah, aber du startest die Methode über das Testframework. Ich weiß nicht genau, was das Ding macht, aber irgendetwas sagt mir, dass deine Komponente sich nicht auf einem GameObject befindet. Tests packst du auch einfach nicht in MonoBehaviours. Nimm also die Superklasse da raus und baue dir für den Test ein GameObject.

private GameObject gameObject;

[SetUp]
public void Setup()
{
    gameObject = new GameObject("Test Object");
}

Denke auch daran, das Ding wieder zu zerstören:

[TearDown]
public void Teardown()
{
    Object.Destroy(gameObject);
}

 

Share this post


Link to post
Share on other sites

Erstmal danke an dich Sascha.

Ich bin jetzt mal ein Schritt weiter gekommen:

    public class ToolTipWindow_Test : MonoBehaviour
    {
        private GameObject toolTip;
        [SetUp]
        public void Setup()
        {
            toolTip = new GameObject("Panel_ToolTip");
            toolTip.gameObject.AddComponent<ToolTipText>();

            //toolTip.GetComponent<ToolTipText>().Start();
            toolTip.GetComponent<ToolTipText>().toolTipText = gameObject.AddComponent<Text>();
            toolTip.GetComponent<ToolTipText>().toolTipPanel = new GameObject("Panel");
        }


        [Test]
        public void CheckTextInTooltip_False()
        {
            bool result = false;

            result = toolTip.GetComponent<ToolTipText>().ToolTipTextInputCheck("");
            Assert.IsFalse(result);
        }

        [Test]
        public void CheckTextInTooltip_True()
        {
            bool result = false;

            result = toolTip.GetComponent<ToolTipText>().ToolTipTextInputCheck("TEST");
            Assert.IsTrue(result);
        }

        [TearDown]
        public void Teardown()
        {
            Object.DestroyImmediate(toolTip);
        }
    }

 

Einziges Problem, das ich noch habe ist folgender Abschnitt:

toolTip.GetComponent<ToolTipText>().toolTipText = gameObject.AddComponent<Text>();

Lagere ich das ganze in eine Funktion aus welche in der zu testenden Klasse ist, und rufe die Funktion im Setup auf, habe ich kein Problem mehr.
Ich wollte das ganze allerdings auch das ganze in der TestSetup machen.

Share this post


Link to post
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...

×
×
  • Create New...