I mean to capture my current early 2022 thoughts on writing tests intended to be run by automated processes created for Unity based video games. I want to capture my thoughts so I can share this and receive feedback. I know I haven’t found a holistic philosophy around testing. I know I can be better. If you can help, please do!
Pit stop: Where is all the content?
I love researching topics, buying books, and seeing what content “influencers” say on topics. When I investigate this topic I find very little content. Are content creators disinterested in this topic? The ones that do create content seem to be stuck showing testing setup and trivial examples, such as testing a function that adds two numbers. I’m sure there is a deep dive out there but I can’t seem to find it.
There is hope. Someone shared this high quality Unity talk where they discuss how they structured their code to support testing, and why that’s important.
My conclusion is that testing is just a subject that requires more thought and consideration from the community.
With the help of books like Unit Testing: Principles, Practices, and Patterns, Growing Object Oriented Software Guided by Tests, and Dependency Injection: Principles, Practices, and Patterns I have synthesized my own nascent testing philosophy for unity.
Does it even make sense to write tests for video games?
For me the answer is “yes.” I typically share this short blog from an EA employee: 3 Lines of Code Shouldn’t Take All Day
The answer for you may be “no” and I think that’s just fine. I truly see merits in the styles of thinking that lead to “yes” and that lead to “no.” I think both answers stem from valid worldviews.
Do. What. Works. For. You.
Types of Testing
In Unity there are many types of code one can write. Right now I simplify by declaring four types of code. Attachable components derive from the familiar MonoBehaviour (MB). Custom project assets are defined by ScriptableObjects (SO) and typically work in tandem with MBs. Editor scripts derive from Editor. The humble vanilla C# code serve as a way of modeling a solution space outside of the Unity game engine.
The Unity Test Framework affords a matrix of four types of test suites. First, one must decide if a test is an Edit Mode test or a Play Mode test. Afterwards, one must decide if the test they are writing requires an asynchronous execution context ([UnityTest]
) or a synchronous one ([Test]
). Note: the unity test framework 2.0 consolidates Edit Mode and Play Mode testing and leverages attributes instead to make the same types of declaration as above.
- Synchronous edit mode tests are the fastest suite
- Asynchronous edit mode tests are slightly slower
- Synchronous and asynchronous play mode tests are the slowest option but allows the Unity game engine to execute lifecycle methods
Unit Tests
Vanilla C# code gets to leverage edit mode tests. A tester has full control over the construction and function calls.
Editor tests are tests that require the [UnityTest]
attribute but can still be run as an Edit Mode test. I honestly haven’t written enough editor scripts to have an example test for them.
I have learned not to fight and to just add MonoBehaviour and ScriptableObject as Play Mode tests. This allows the Unity engine to call all relevant lifecycle methods. You can do this yourself by making private
methods into public
ones but… Been there, there be dragons, and no thanks.
Another “gotcha” that I learned to appreciate is the Order of Execution for the unity class lifecycle methods. Basically, if you want to ensure your Awake
or Start
methods are called, you need to wait a frame. I usually yield return null
or yield return new WaitForEndOfFrame()
to wait a frame.
Anyways, since this is a unit test I usually create a new GameObject
and AddComponent
to add the MB I want to test. I save the conceptualization of a Game Object as a means of dependency injection and service locator for another time.
Here is an example ScriptableObject test.
Integration Tests
This is where I think I have some interesting insight. I think prefabs serve as a perfect unit for integration testing. I like to instantiate an instance of a prefab and write tests against prefabs that describe higher level behavior. For example, here is a test where I assert that this “player” prefab can be moved by input from the new unity input system.
At this time, I have not written a unity project that collaborates with external systems. When I do I plan on using abstract interface techniques to hide the collaboration with the external system in a way that I can mock out that collaboration. To avoid a leaky layer of abstraction I will return my own domain objects instead of responses directly from whatever system I am collaborating with.
Acceptance Testing
Unity Scenes serve as the level I want to write tests. Right now I typically ensure expected game objects exist within the scene and hope that the lower level tests have ensured a certain behavior of game entities.
Conclusion
I’m still figuring it out. I’m sure the type of content I hope for is it out. However; I’m perfectly okay figuring out my own strategy. If you have any input or suggestions, I would appreciate receiving feedback. Thank you in advance!
I hope some found this content interesting.