Trouble Unit Testing Scriptable Object in UnityTest

After watching a bit of the 2017 Unite talks from last year, I was interesting in trying out Unit testing in Unity. Since I work with Nunit with C# in my profession and I like the feel of TDD, I thought this would be a snap. Sadly, I hit a snag right out of the gates.

I wanted to created a simple Runtimeset ScriptableObject (similar to examples in Ryan Hipples’s talk “Unite Austin 2017 - Game Architecture with Scriptable Objects” talk) and so I implemented the unit test code below:

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

public class RuntimeSetTest
{
    [UnityTest]
    public IEnumerator RuntimeSetwithContructor_emptySetatConstruction()
    {
        RuntimeSet<RuntimeSetItem> runtimeset = ScriptableObject.CreateInstance<RuntimeSet<RuntimeSetItem>>();
        System.Threading.Thread.Sleep(5000);
        Assert.IsNotNull(runtimeset);
        yield return null;
    }
}

Note: I put the System.Threading.Thread.Sleep(5000); to see if there was some kind of lag with unity creating scriptable objects. I have tried with or without this to no effect on the result

The RuntimeSet and the RunTimeSetItem are just as barebones:

using System.Collections.Generic;
using UnityEngine;

public class RuntimeSet<T> :ScriptableObject where T : ScriptableObject
{
    public List<T> Items = new List<T>();
}

using UnityEngine;

public class RuntimeSetItem : ScriptableObject
{
}

Looks simple enough. I should see that the Assert.IsNotNull(runtimeset) returns true, right? However it does not.

What is actually going on here? Does the ScriptableObject.CreateInstance not work within the [UnityTest] context? How would we Unit Test ScriptableObjects then? Or is this some kind of strange Garbage collection quirk that I am running into?

Or am I missing something even more obvious? (wouldn’t be the first time)

Thanks for your help.

I’m pretty sure the problem is that you can’t instantiate a ScriptableObject with a generic type parameter. Unity needs to know the ScriptableObject’s full type beforehand in order to serialize it. One workaround is to use a non-generic subclass that implements the generic type you want. In other words:

//This will not work
runtimeset = ScriptableObject.CreateInstance<RuntimeSet<RuntimeSetItem>>();

//But this will work
public class SpecificRuntimeSet : RuntimeSet<RuntimeSetItem> { }

runtimeset = ScriptableObject.CreateInstance<SpecificRuntimeSet>();
2 Likes

Yep this did it. Thanks!

Would have been nice if Unity threw an exception for such a unintuitive limit in functionality, but maybe I’m asking too much.

1 Like

But how do I use a specific instance of a ScriptableObject in a UnitTest? A new one wouldn’t do, since I want to subscribe to an event that’s raised from my object under test in the ScriptableObject.