Writing Unit Tests with NSubstitute (Unity)

z4gon
z4gon

Use the Unity Test Framework, NUnit and NSubstitute to test your game.

Cover Image for Writing Unit Tests with NSubstitute (Unity)

References

Table of Content


Install the Unity Test Framework

  • Make sure the Test Framework package is installed in the project and updated.

Picture


Assembly Definitions

  • Create two Assembly Definitions, one for the Edit Mode tests and another for the Play Mode tests.
  • Edit Mode tests only run in the Unity Editor and have access to the Editor code in addition to the game code.

Picture Picture Picture

  • Make sure to add a reference to the Assembly Definition with the code for your game, so you can tests its classes.

Picture


Tests Files

  • Create test files using the template or write your own from scratch.

Picture Picture Picture Picture

Picture


Add NSubstitute

  • You will need to obtain the .dll from the NuGet registry manually, and add it to the Unity project as a Managed Plug-in (Which is a .NET assembly).
  • Go to the NuGet package for NSubstitue and download the .nupkg

Picture

  • Change the extension to .zip to be able to extract its contents.

Picture

  • Copy over the .dll corresponding to the Api Compatibility Level set in the Player Settings of your project. (Most likely .NET Standard 2.1)
  • Repeat the process for any dependency of the library.

Picture Picture

  • Reference the Assembly in the Assembly Definition of your tests, to be able to use NSubstitute.

Picture


Tests Examples

  • [UnityTest] tests allow you to skip a frame in the simulation by doing yield return null, or skip many doing yield return new WaitForSeconds().
  • [Test] tests don't run the simulation of the game, you can't skip frames.
1[UnityTest] 2public IEnumerator test__on_trigger_enter__invokes_gun_reclaim_bullet() 3{ 4 var gun = Substitute.For<IGun>(); 5 6 var bullet = CreateBullet(); 7 bullet.gun = gun; 8 bullet.speed = 20.0f; 9 10 var limits = CreateProjectilesLimit(); 11 12 yield return new WaitForSeconds(0.05f); 13 14 gun.Received().ReclaimBullet(bullet); 15 16 GameObject.Destroy(bullet.gameObject); 17 GameObject.Destroy(limits.gameObject); 18} 19 20public static Bullet CreateBullet() 21{ 22 var obj = new GameObject("Bullet"); 23 obj.layer = LAYER_PLAYER_PROJECTILES; 24 25 var rigidbody = obj.AddComponent<Rigidbody>(); 26 rigidbody.useGravity = false; 27 28 var collider = obj.AddComponent<BoxCollider>(); 29 collider.size = new Vector3(1.0f, 1.0f, 1.0f); 30 collider.isTrigger = true; 31 32 var bullet = obj.AddComponent<Bullet>(); 33 34 return bullet; 35} 36 37private BoxCollider CreateProjectilesLimit() 38{ 39 var obj = new GameObject("ProjectilesLimit"); 40 obj.layer = Utils.LAYER_PROJECTILES_LIMITS; 41 42 obj.transform.position = new Vector3(0.0f, 0.0f, 1.1f); 43 44 var collider = obj.AddComponent<BoxCollider>(); 45 collider.size = new Vector3(5.0f, 5.0f, 1.0f); 46 collider.isTrigger = true; 47 48 return collider; 49}