This is a bit of a complicated problem, but here’s what I’m looking to do:
I want to affect how my overall project runs based on whether it’s being started normally (e.g. clicking play) vs being started via the Test Runner in PlayMode.
In my case, I have application-level setup that’s being done via static constructors and via the RuntimeInitializeOnLoadMethod attribute. From these, my application sets up core systems, does some dependency injection, sets up what’s basically a service locator for MonoBehaviours, etc. The problem is, I don’t want to kick this off automatically when I’m running tests. Instead, I want to create only the systems I need for the test - which is the whole point of using DI :). This will allow me to easily mock up systems and properly unit test and integration test items while having full control over the test.
So, somehow my application needs to know not to run code in the case that it was kicked off from the Test Runner. How do I do that?
Here’s my solution. I’m posting it in case it’s useful to anyone else, and also in case someone else has a better idea for how to do it.
It starts with IPrebuildSetup and IPostBuildCleanup. These are great as IPrebuildSetup runs before everything else, including static constructors. However, it runs so early that they run before the assembly is reloaded for the test. That means that, if you try to set a variable value, it won’t persist into the actual test.
So from there I set a PlayerPref (which I call “RuntimeTest”) bool to true during the test, and back to false at IPostBuildCleanup.
Then, I use a static constructor in a base class (that all of my runtime tests derive from) which affects the actual project. NOTE: in my case, Application is Havenly.Studio.Application, not UnityEditor.Application.
Here’s my code:
public abstract class RuntimeTestSuite : IPrebuildSetup, IPostBuildCleanup
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
private static void SetApplicationShouldRun()
{
Application.ShouldRunSetup = !EditorPrefs.GetBool("RuntimeTest");
}
public void Setup()
{
Debug.Log("Got test setup");
EditorPrefs.SetBool("RuntimeTest", true);
}
public void Cleanup()
{
Debug.Log("Got test cleanup");
EditorPrefs.SetBool("RuntimeTest", false);
}
}
Hope that helps someone else!
Does anyone have any better ideas about how to do this that doesn’t involve EditorPrefs? The only downsides I can see here are:
- If the editor itself crashes during a test, the EditorPref persists
- In a broader sense, EditorPrefs persist when you close the application. This seems a bit unsafe
To help with this, I have a Debug message when the application starts and Application.ShouldRunSetup is true, just to make it obvious why the application isn’t working.