Unit Testing MonoBehaviours

So there was this blog post here:
https://blogs.unity3d.com/2014/06/03/unit-testing-part-2-unit-testing-monobehaviours/

It recommends separating logic from MonoBehaviours because you can’t unit test MonoBehaviours because ‘Every time you try to instantiate a MonoBehaviour derivative you will get a warning saying that it’s not allowed.’

Well of course, your not supposed to instantiate MonoBehaviours, your supposed to add them to GameObjects with AddComponent.

And it seems to me that works just fine and that you can actually Unit Test a MonoBehaviour. You create a new GameObject, then AddComponent the MonoBehaviour onto that GameObject, run whatever tests you want on that added MonoBehaviour, when the test is done it automatically cleans up the GameObject and Component.

Seems to work just fine without any issues at all…

I needed Awake, Start and Update to be called in a few of them. So I wrote a utility method ‘CreateMonoBehaviourAndRun’ which creates the MonoBehaviour through AddComponent and then uses reflection to the call the private Awake, Start and Update method.

Seems to work just fine with any issues at all…

I am kind of wondering whats going on here? Why did that Unity blog post insinuate that you can’t Unit Test MonoBehaviours? Did the author of it really just not know that your supposed to use AddComponent and not Instantiate on MonoBehaviours?

Also, in case you might say ‘But your not supposed create new GameObjects in a Unit Test!’ Which seems to work just fine without any isssues. But if you need more reason then that. If you make a Unit Test through Unity’s built in Create > Editor Test C# Script option. This is the default code template for a Unit Test:

using UnityEngine;
using UnityEditor;
using NUnit.Framework;

public class NewEditorTest {

    [Test]
    public void EditorTest()
    {
        //Arrange
        var gameObject = new GameObject();

        //Act
        //Try to rename the GameObject
        var newGameObjectName = "My game object";
        gameObject.name = newGameObjectName;

        //Assert
        //The object has a new name
        Assert.AreEqual(newGameObjectName, gameObject.name);
    }
}

The default code template is showing an example of making a new GameObject.

So whats up with that blog post?

In general MonoBehaviours tend to be challanging to effectively isolate from the rest of the game. MonoBehaviours can only exist in a scene with GameObjects. Making unit testing more difficult.

If you can isolate MonoBehaviours well enough for a unit test to be meaningful, then go ahead and unit test then. There is nothing inherently wrong with running unit tests in MonoBehaviours.

The author may just not consider such habits adequate for testing.

Not ‘pure’ enough or something.

As the verbage BoredMormon uses… unit testing is about ‘isolation’, and a GameObject in a scene isn’t truly isolated. The author may consider it better to ‘isolate’ your logic away from the MonoBehaviour to get what they might consider a better unit test.

But yeah, go about it however you want. The topic of unit testing comes with a mix bag of ideologies and opinions on what is the RIGHT way of doing it. If you think your unit test is sufficient… do it… you’re not the author of that blog. If you TRULY want to know why the author feels as such… ask the author!

1 Like

More reading:
news (2012)
You Can Unit Test MonoBehaviours (OP article ?) (2017)

I’m new to testing, why not just instantiate the gameobject containing our component to test on the scene so it call itself Awake,Start, Update ?

You can do that, but there are some quirks to it.

The difficulty is the complexity of interacting components, such as all kinds of dependencies a script may need to resolve in order to run its functionality.
It’s already been stated, that the easiest “units” to test are those that have very little or none dependencies on other entities and/or units.

The reason is, that those dependencies should usually not take uncontrollable influence on a particular unit test, the tests should usually be run without negative (let’s just say undeterministic) influence of anything else that happens to be required for the component to work.

Instead, you’re better off simulating these dependencies (and the circumstances you need) with more deterministic stubs that you have full control of, to ensure you can trigger exceptional behaviour manually and reach different (best: all) code-paths. Code coverage analysis tools are great to find weaknesses in your test setup, it’s important to use such a tool.

It’s a hell lot of work if you wanna do it right, but it also helps to find the most akward, funny and yet trivial bugs that you may never have thought of. :slight_smile: Sometimes I already had a good laugh about my self. :stuck_out_tongue: