5
\$\begingroup\$

I had problems with decoupled scripts and exposing interfaces in Unity, and these answers helped me a lot. But now, since I don't have hard-coded references in my scripts, I wanted to write unit tests for my code.

This presented a problem - I can't instantiate MeonoBehaviours using new because of how the engine works. What is more, I can't mock out anything from the Unity API.

I found a great blog post by Tomek Paszek. He suggests using the Humble Object pattern, and it seems reasonable. Still, maybe thats just me, because I haven't used that pattern too much, when using the Humble Object pattern even simple code seems to be very complex.

Are there other ways to write testable MonoBehaviors?

\$\endgroup\$
  • \$\begingroup\$ Since a year has passed, may I ask what did you end up doing? \$\endgroup\$ – Isaac Remuant Mar 29 '16 at 16:50
  • 2
    \$\begingroup\$ @IsaacRemuant I just don't test. :( As it turns out, I would have to really get out of my way to do automated tests, and the gains from doing them do not outweigh the costs, at least in small indie projects. It's very different from regular business development. \$\endgroup\$ – K.L. Mar 30 '16 at 7:34
  • 1
    \$\begingroup\$ I recently wrote a blog post about this subject precisely. \$\endgroup\$ – rygo6 Mar 28 '17 at 7:11
  • \$\begingroup\$ In 2017 Unity added a solution for this. The documentation is sparse but you can find examples on how to use it. See the documentation for MonoBehaviourTest<T0>, a question about it on the forums, and Unity Test Runner. \$\endgroup\$ – Oliver Sep 28 '18 at 12:54
6
\$\begingroup\$

Use the unit test framework 'Unit Test Tools' provided by Unity.

You're right you can't create new MonoBehaviours on their own, but why not use an empty GameObject and use AddComponent<MyMonoBehaviour>() and then run your tests?

Alternatively you can create the bulk of your logic inside your own classes. Then your MonoBehaviour scripts will just use composition to have a local instance of the class that represents their functionality. Your unit testing can also just make a local instance of the class. Though, this sounds more messy than the Humble Object pattern suggested in the blog post.

Yes, creating a testing framework and implementing unit tests can be complex. But once you have things in place, adding additional unit tests is fairly trivial.

\$\endgroup\$
1
\$\begingroup\$

There's another option to Unit test MonoBehaviours without calling the constructor (using FormatterServices). Here's a small helper class that creates testable MonoBehaviours:

public static class TestableObjectFactory {
    public static T Create<T>() {
        return FormatterServices.GetUninitializedObject(typeof(T)).CastTo<T>();
    }
}

Usage:

var testableObject = TestableObjectFactory.Create<MyMonoBehaviour>();
testableObject.Test();
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.