Search Unity

Question Unit Test : How to create a custom Test Attribute that creates Edit & Play Unit test?

Discussion in 'Testing & Automation' started by Sangemdoko, Mar 5, 2020.

  1. Sangemdoko

    Sangemdoko

    Joined:
    Dec 15, 2013
    Posts:
    220
    Hi,

    I'm working on a big project and I have a few hundred play mode tests. Recently I decided to do a big refactor and I should now be able to convert many of my play mode tests to edit mode tests. My understanding is that EditMode tests are much faster compared to playmode tests.

    But instead of converting the tests completely to EditMode I thought I could make them both PlayMode and EditMode test.

    My solution was to do this In the play mode assembly:
    Code (CSharp):
    1. [UnityTest] public virtual IEnumerator MyTestFunction_Unity() {
    2.     yield return Utility.LoadNoDatabaseTestScene();
    3.     MyTestFunction (Utility.GetInventorySystemManager());//gets it from the loaded scene its a Monobehavior singleton
    4. }
    5. [Test] public void MyTestFunction () { MyTestFunction (new UnitTestInventorySystemManager()); }
    6. public void MyTestFunction (IInventorySystemManager manager){...}
    Then in the edit mode assembly I inherit the class and override the function with the [UnityTest] attribute so that it can hide it. This way I am left only with the test with [Test] attribute.

    So that works... but its a lot of clutter especially if I'll be doing this for 100+ tests, and I could technically make a human error by testing the wrong function if I'm not careful when copy pasting the two test functions.

    I was wondering if there was a way for me to create a custom attribute that would essentially do all of that for me.

    So that I could be left with
    Code (CSharp):
    1. [MyCustomInventorySystemManagerTest]
    2. public void MyTestFunction (IInventorySystemManager manager){...}
    I tried inheriting from UnityTestAttribute and I played around a bit but I do not know enough about Unit test building to get anything I want working.

    So I was wondering if any of you would be able to help me with this.
    Thanks for your time
     
  2. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,652
    I'm not clear on what problem you're trying to solve by having the two functions. You're trying to make it so that in playmode the scene gets loaded, but in edit mode it doesn't, and you want to just use the singleton from the currently loaded/active scene?
     
  3. Sangemdoko

    Sangemdoko

    Joined:
    Dec 15, 2013
    Posts:
    220
    So the play mode test should load a scene and get the singleton, Then it uses that as parameter for the test

    The edit mode test creates a new object which inherits the same interface as the singleton so that I can also use it as a parameter for my test.

    This allows me to test the Monobehavior singleton as parameter in play mode and in edit mode I can test using a custom object used purely for testing as paramter.

    I hope that makes sense
     
  4. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,652
    In that case, I would unify the two in a SetUp and use a bit of conditional logic. Maybe something like:

    Code (csharp):
    1.  
    2. [UnitySetUp]
    3. public IEnumerator SetupInventorySystemManager()
    4. {
    5.     if (Application.isPlaying) {
    6.        yield return Utility.LoadNoDatabaseTestScene();
    7.        _inventorySystemManager = Utility.GetInventorySystemManager();
    8.     }
    9.     else
    10.     {
    11.        _inventorySystemManager = new UnitTestInventorySystemManager();
    12.     }
    13. }
    14.  
    You could also do #if UNITY_EDITOR around the else-block if that test class cannot exist in a player build. Would that solve your problem?
     
  5. Sangemdoko

    Sangemdoko

    Joined:
    Dec 15, 2013
    Posts:
    220
    Oh, I didn't know UnitySetUp was a thing I thought you could only do [SetUp].
    I probably should have thought about using if(Application.isPlaying)
    That might just work for me Thank you!