Introduction To Unity Unit Testing

Learn all about the Unity Test Framework and how to set up Unit Tests in your Unity projects. By Ben MacKinnon.

5 (1) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 2 of 5 of this article. Click here to view the first page.

What’s in a Test Suite?

As you learned above, a unit test is a function that tests the behavior of a small, specific, set of code. Since a unit test is a method, it needs to be in a class file in order to run.

The Test Runner will go through all your test class files and run the unit tests in them. A class file that holds unit tests is called a test suite.

A test suite is where you logically divide your tests. Divide your test code among different logical suites — like a test suite for physics and a separate one for combat. For this tutorial, you only need one test suite — and it’s time to create it. :]

Setting Up the Test Assembly and the Test Suite

Select the Tests folder and in the Test Runner window, click the Create Test Script in current folder button. Name the new file TestSuite.

unittest create testsuite

You might notice that there was already another file in the Tests folder. Unity also created the file Tests.asmdef when you created the folder in the first step. This is an assembly definition file, and it’s used to point Unity to where the test file dependencies are. This is because your production code is kept separate from your test code.

If you run into a situation where Unity can’t find your test files or tests, double-check to make sure there’s an assembly definition file that includes your test suite. The next step is setting this up.

To ensure the test code has access to the game classes, you’ll create an assembly of your game code and set the reference in the Tests assembly. Click the Scripts folder to select it. Right-click this folder and choose Create ▸ Assembly Definition.

Create Assembly Definition

Name the file GameAssembly.

unittest create assembly file

Next, you need to add a reference of the GameAssembly to the Tests assembly. To do this:

  1. Click the Tests folder, and then click the Tests assembly definition file.
  2. In the Inspector, click the plus button under the Assembly Definition References heading.
  3. Drag the GameAssembly into the new field.

Drag GameAssembly

You’ll see the GameAssembly assembly file in the references section. Click the Apply button at the bottom of the Inspector window to save these changes.

Apply button

This process lets you reference the game class files inside the unit test files — and so now you can write tests.

Writing Your First Unit Test

Double-click the TestSuite script to open it in a code editor. Before you get started, read through the template code in the file. You’ll see two methods in there: TestSuiteSimplePasses() and TestSuiteWithEnumeratorPasses. Notice they have different attributes attached to them — [Test] and [UnityTest], respectively. The comments in the template explain this, but while a [Test] case executes like a regular method, [UnityTest] acts as a Coroutine in which you can use yield statements to wait for certain things to happen. This becomes particularly useful when testing physics code, which you’ll do now!

Replace all the code in TestSuite.cs with the following:

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

public class TestSuite
{

}

What tests should you write? Truthfully, even in this tiny little Crashteroids game, there are quite a lot of tests you could write to make sure everything works as expected. For this tutorial, you’ll focus on hit detection and core game mechanics.

Note: When you write unit tests on a production-level product, it’s worth taking the time to consider all the possible edge cases you need to test for all areas of your code.

For the first test, it’s a good idea to make sure that the asteroids actually move down. It would be really hard for the asteroids to hit the ship if they’re moving away from it! Add the following method and private variable to the TestSuite script and save:

// 1
private Game game;

// 2
[UnityTest]
public IEnumerator AsteroidsMoveDown()
{
    // 3
    GameObject gameGameObject =
        Object.Instantiate(Resources.Load<GameObject>("Prefabs/Game"));
    game = gameGameObject.GetComponent<Game>();
    // 4
    GameObject asteroid = game.GetSpawner().SpawnAsteroid();
    // 5
    float initialYPos = asteroid.transform.position.y;
    // 6
    yield return new WaitForSeconds(0.1f);
    // 7
    Assert.Less(asteroid.transform.position.y, initialYPos);
    // 8
    Object.Destroy(game.gameObject);
}

There’re only a few lines of code here, but there’s a lot going on. So, take a moment and make sure you understand each part:

  1. This is a class-level reference to the Game class. This gives you access to key game logic for testing.
  2. This is an attribute, as described above. Attributes define special compiler behaviors. It tells the Unity compiler that this is a unit test. This will make it appear in the Test Runner when you run your tests.
  3. Creates an instance of the Game. Everything is nested under the game, so when you create this, everything you need to test is here. In a production environment, you will likely not have everything living under a single prefab. So, you’ll need to take care to recreate all the objects needed in the scene.
  4. Here you are creating an asteroid so you can keep track of whether it moves. The SpawnAsteroid method returns an instance of a created asteroid. The Asteroid component has a Move method on it (check out the Asteroid script under Assets / Scripts if you’re curious how the movement works).
  5. Keeping track of the initial position is required for the assertion where you verify if the asteroid has moved down.
  6. As you’re using a UnityTest coroutine, you have to add a yield statement. In this case, you’re also adding a time-step of 0.1 seconds to simulate the passage of time that the asteroid should be moving down.
  7. This is the assertion step, where you are asserting that the position of the asteroid is less than the initial position (which means it moved down). Understanding assertions is a key part of unit testing, and NUnit provides different assertion methods. Passing or failing the test is determined by this line.
  8. Be sure to clean up after yourself! It’s critical to delete or reset your code after a unit test so that when the next test runs there aren’t artifacts that could affect that test. Deleting the game object clears away anything else that might be created.