Writing Unit Tests for My Game in Unity
Assuming you already know basic C# and Unity
Most of you must have already known that I am currently developing a game in Unity, called Night at the Clownville. It is a 3D low poly first person shooter, survival horror game where you get trapped in a theme park and you will struggle to get out. Don’t worry, I won’t be promoting it here (sorry, I just did), just check it out on our Instagram page! What I do want to say in this article is the topic of unit tests in Unity. Sit tight and read on!
Why test the code when it works perfectly fine?
I came across the topic of unit tests during my first internship experience as a firmware developer working on HDDs. However, the concept of unit test was something I’m afraid of since I knew nothing about it. Like most developers, I used to laugh at the idea of “testing” code. “Why test the code when it works perfectly fine?” is exactly the question I had in my mind. Cut to the chase, I only knew the importance of unit tests when I started developing Night at the Clownville.
KISS (Keep it short and simple)
To put it simply, a unit test is a piece of code testing a single “unit” of your code. Sounds simple and stupid, right? It is supposed to be that simple. But not stupid. As a matter of fact, a unit test is used to validate a small, logical snippet of code so it performs exactly the way we want it to in specific scenarios. Well, it would be ideal to generalize the piece of code so it can fit into any situations. But it is essential to keep your code short and simple so it performs well for each and only task — KISS (Keep it short and simple).
Now, let’s get our hands dirty by writing a unit test for a simple function in Unity. This unit test is also what I use to evaluate my short snippet of code here. To get started, let’s set up the unit testing feature in Unity. The good part of Unity is that it provides the Test Runner, a unit testing feature that uses the NUnit framework in C#. It is fundamentally an open-source test framework that supports unit testing in isolated modules. Read more here.
In Unity, head on over to Window > General > Test Runner. For this article, let’s only write a unit test in Edit mode, where we can test any script that does not have to run in Play Mode. In Test Runner, hit the “Create Test Script in current folder” button and this will allow Unity to do two things:
- Create an assembly definition file (This is important to keep file dependencies separate from our production code).
- Create a test script (We can rename this to whatever we want, just give it a relevant name).
This is what you will get when the test script gets created. If the second function catches your eye, it is because it is a Coroutine function. Fret not, we are not going to use this today, feel free to look it up as it is widely used when writing scripts for your game.
As you can already see from the line comment inside the first function — use the Assert class to test conditions, that is what we are going to use today. For this article, I am going to write a unit test for my Health component. As with any character be it the player or NPC or the enemy, it requires a Health component. We would set their health individually before or during runtime but what is more important is that we need to ensure our function that sets the health runs correctly and returns the right health number. Then, as with any character, it is able to take damage that results in a decrease in health or take a health potion to increase its health. So, let’s define our rules here:
- When health is instantiated before or during runtime, it should return the correct health number and be less than the maximum health.
- If and only if health is above zero, it can take damage and it should return the decremented health.
- If and only if health is below maximum health and above zero, it can be incremented and it should return the incremented health.
Of course, then, we will need to also take care of the corner cases, like:
4. If health is below zero, check if it can take damage and it should return zero instead of a negative number.
5. If health is equal to or above the maximum health and the player takes a health potion, it should return the maximum health.
Clearly, we need a Health component.
For the sake of simplicity of this article, we will just work with this constructor which creates a Health object by giving it:
- the current health the game object should have
- the maximum health the game object should have
- a health change event (this is Unity event system, which we will get into in a future article)
- an event that caters to when the health turns zero
Let’s set our starting health and test it.
From here, you can clearly see the TestCase attribute above the test function. The integers within the braces correspond to the argument of the function. Hence, we can pass in multiple sets of numbers to test the function, but we are definitely going to cover all the corner cases in as short test cases as we could. Using Assert.IsTrue(), we only want to compare if the health variable created is indeed equal to what we pass in in the TestCase attribute.
Ignore the TakeDamage() function, but just know that damage is caused and we want to make sure the returned health is equal to the starting health minus the damage. Simple!
Similarly with incrementing health, we want to make sure the returned health is equal to the sum of the starting health and the increment, assuming it is below the maximum health.
When the player or any character has a health below zero and yet still receives damage, the health should be equal to zero no matter what.
Similarly, if the health is already above the maximum predetermined value, it should be set to the maximum value, even with further increments.
When you are done with writing the entire test suite consisting of multiple tests, it is time to run it in Unity!
Simply press “Run Selected” after selecting the Health Test you have just created and voila! The last thing we want is to have failed tests, so just make sure you know what test you are running.
In this article, I have shown how to perform unit tests for your components in Unity using the built-in Test Runner. Often neglected, unit tests are a surefire way to keep your code short and simple since all they test is by unit of function. I hope you will have fun writing unit tests as it is still one of the best thing that I enjoy once in a while. Happy developing!