Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Testing private fields with Unity Test Tools

Discussion in 'Editor & General Support' started by IDoAllTheWork, Nov 17, 2015.

  1. IDoAllTheWork

    IDoAllTheWork

    Joined:
    Jun 20, 2015
    Posts:
    19
    Hello everybody,

    I'm currently trying to get into the Unity Test Tools. After having read and watched some tutorials and I'm now trying to get them to work in a small sample project. But I don't know how to test private methods.

    E.g. I want to check, if the health of the player is set correctly in the Awake function.

    Code (CSharp):
    1. [Test]
    2. public void AssertAwake()
    3. {
    4.     Health health = new Health();
    5.    
    6.     Assert.That(health._currentHitPoints == health._initialHitPoints);
    7. }
    But both members are private, so this obviously does not work. But how can I test private methods / use private members during tests? The only solutions I found are not working with Unity objects.

    And btw, Unity warns me that "You are trying to create a MonoBehaviour using the 'new' keyword. This is not allowed. MonoBehaviours can only be added using AddComponent(). Alternatively, your script can inherit from ScriptableObject or no base class at all". Should I care about that or just ignore it, as this is just a unit test?
     
    liortal likes this.
  2. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,851
    Great to hear your getting into the Unity Test Tools, so am I :D
    Generally Unit tests like this are designed to test a public API so testing a private field is a little tricky but not impossible.
    Firstly why is it private, can you not provide a public property to allow access? If you really must test the private value then it is possible to get to private variables in .Net using reflection see here.
    Finally to address the last question you should not instantiate MonoBehaviours using new. That means this code is invalid:
    Code (csharp):
    1. Health health = new Health();
    Instead do something like this:

    Code (csharp):
    1. GameObject go = new GameObject();
    2. Health health = go.AddComponent<Health>();
    Happy testing!
     
    IDoAllTheWork likes this.
  3. IDoAllTheWork

    IDoAllTheWork

    Joined:
    Jun 20, 2015
    Posts:
    19
    Thank you for taking the time to reply!

    Yes I can, but wouldn't that break the encapsulation principle? In my recent test project (which is a very simple project I'm using solely to get familiar with the Unity Test Tools) the Health script is meant to handle the object health all by itself. When another object able to damage my object wants to damage it, it calls the method TakeDamage(int damage) from the interface. Anything else is done internally in the Health-component, which is why I thought it would be good design to hide health from the world. But that makes it difficult to check, if the TakeDamage-function actually changed health correctly.


    Ok, this seems to work, but only for nullable types (Visual Studio tells me so, DuckDuckGo confirmed it ;-) ). Do you kow how to make this work with non-nullable types?

    OMG, I'm so stupid (or was too tired)... now that you say it, I remember reading it on the Unity Blog... :-D

    Edit: Removed the off-topic parts, which I'll put into a separate thread.
     
    Last edited: Nov 21, 2015
  4. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,851
    So its still possible to allow public access and also protect the variable. Use a property with a get but no set. Like so:
    Code (csharp):
    1. public int Health{ get{ return _health; } }
    If you want to keep a variable private but still allow access to it in the inspector you can do it with the [SerializeField] attribute.
    So your class could look something like this:
    Code (csharp):
    1.  
    2. public class Health : MonoBehaviour
    3. {
    4.     [SerializeField]
    5.     private int health = 100;  
    6.  
    7.     public int CurrentHealth
    8.     {
    9.         get
    10.         {
    11.             return health;
    12.         }
    13.     }
    14. }
     
    liortal likes this.
  5. crazyrems

    crazyrems

    Joined:
    Feb 25, 2013
    Posts:
    5
    Sorry for digging out this old thread but I found interesting ways to achieve that.

    I make public accessors that are only available within the context of the tests by using preprocessor macros.

    Code (CSharp):
    1. class Health
    2. {
    3. ...
    4. #if UNITY_INCLUDE_TESTS
    5.     // Accessors for testing purpose
    6.     public int CurrentHitPoints_test()
    7.     {
    8.         return _currentHitPoints;
    9.     }
    10.     public int InitialHitPoints_test()
    11.     {
    12.         return _initialHitPoints;
    13.     }
    14. #endif
    15. }
    You can find them in your test assembly definition asset and change them as well.


    You can even separate those test methods in another file by using partial declaration if you don’t want to mess your logic with your tests.
     
  6. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,851
    I would suggest using internals to do this now.
    Mark the accessors internal and then add internals access to your Test Asmdef with
    InternalsVisibleTo
     
    noy-g and crazyrems like this.