Unity Test Runnerの色々な小技をまとめました。
- 複数の入力値をテストする
- 特定プラットフォームで実行したときだけテストする
- MonoBehaviourを使ったテストを書く
- メモリアロケーションがあるかどうかテストする
- Edit Modeテスト中にPlayモードに入れる
- スクリプトをコンパイルしなおす
- テスト開始・終了時のコールバックを得る
- テスト用のPrimitiveのGameObjectを作る
- 関連
- 参考
Unity 2019.3.5
Test Framework 1.1.11
複数の入力値をテストする
一つのメソッドで複数の入力値をテストするにはValueSource
アトリビュートを使用します。
引数には入力値を返すメソッドかフィールド、プロパティの名前を渡します。
using NUnit.Framework; public class ExampleTest { private static int[] _values = {1, 3, 9}; [Test] public void Test([ValueSource(nameof(_values))]int value) { Assert.That(value * value, Is.LessThanOrEqualTo(10)); } }
上記の例では1と3と9という入力値についてそれぞれテストを行っています。
結果は以下のように表示されます。9のときだけ失敗していることがわかります。
ちなみにNUnitのTestCase
アトリビュートも使えますが、コルーチン(IEnumerator)には使えないのでValueSource
を使っておいた方が無難です。
using NUnit.Framework; public class ExampleTest { [Test] [TestCase(1)] [TestCase(3)] [TestCase(9)] public void Test(int value) { Assert.That(value * value, Is.LessThanOrEqualTo(10)); } }
特定プラットフォームで実行したときだけテストする
特定のプラットフォームでのみ実行するテストを書くにはUnityPlatform
アトリビュートを使います。
using UnityEngine; using UnityEngine.TestTools; public class ExampleTest { [UnityTest] // macとWindowsのエディタ実行時にだけテストする [UnityPlatform(RuntimePlatform.OSXEditor, RuntimePlatform.WindowsEditor)] public void Test() { } }
ただし対象外のプラットフォームでも実行されないだけでコンパイルはされるので注意してください。
MonoBehaviourを使ったテストを書く
MonoBehaviourを使ったテストを書くには、IMonoBehaviourTestを実装したMonoBehaviourクラスを定義した上で下記のようにMonoBehaviourTestを使います。
using System.Collections; using UnityEngine; using UnityEngine.TestTools; public class ExampleTest { [UnityTest] public IEnumerator Test() { // MonoBehaviourTestを使う yield return new MonoBehaviourTest<TestBehaviour>(); } public class TestBehaviour : MonoBehaviour, IMonoBehaviourTest // IMonoBehaviourを実装 { // 一定の座標よりも下になったら終了 public bool IsTestFinished => _target.position.y < -100; private Transform _target; // テスト対象のGameObject生成 private void Awake() => _target = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform; // 毎フレーム位置を下げていく private void Update() => _target.transform.position += Vector3.down; } }
このテストではMonoBehaviour生成時にGameObjectを一つ生成し、
毎フレーム位置を下げていって一定の座標よりも下まで行ったら終了しています。
メモリアロケーションがあるかどうかテストする
メモリアロケーションがあるかどうかをテストするにはAssert.That()
のConstraintに以下のようにIs.AllocatingGCMemory()
を使います。
using NUnit.Framework; using UnityEngine; // Unityが拡張したNUnitのIsのメソッドを使うため明示的に定義しておく using Is = UnityEngine.TestTools.Constraints.Is; public class ExampleTest { [Test] public void Test() { Assert.That(() => { Debug.Log(Vector3.one); }, Is.AllocatingGCMemory()); } }
これはUnityがNUnitのIsクラスを拡張したものになるので、明示的にusing宣言をしておきます。
また実際にはメモリアロケーションがないことをテストすることが多いかと思います。
その場合にはIs.Not.AllocatingGCMemory()
と書けばOKです。
Edit Modeテスト中にPlayモードに入れる
Edit Modeテストの途中でPlayモードに入れたり、Playモードを終了したりするには、
以下のようにEnterPlayMode
とExitPlayMode
クラスを使います。
using System.Collections; using UnityEngine.TestTools; public class ExampleEditorTest { [UnityTest] public IEnumerator Test() { // ここに書いたコードは非再生状態で実行される // PlayModeに入れる yield return new EnterPlayMode(); // ここに書いたコードは再生状態で実行される // PlayModeを終了する yield return new ExitPlayMode(); // ここに書いたコードは非再生状態で実行される } }
スクリプトをコンパイルしなおす
テスト用のスクリプトを動的に生成する場合など、スクリプトをコンパイルしなおすにはRecompileScripts
クラスを使います。
using System.Collections; using System.IO; using UnityEditor; using UnityEngine.TestTools; public class ExampleEditorTest { [UnityTest] public IEnumerator Test() { // 適当にスクリプトファイルを生成 File.WriteAllText("Assets/TestClass.cs", "public class TestClass{}"); // AssetDatabaseに認識させる AssetDatabase.Refresh(); // コンパイルを待つ yield return new RecompileScripts(); } }
また、dllを動的に追加したときなどにUnityの処理を待機するにはWaitForDomainReload
クラスを同じようにして使います。
テスト開始・終了時のコールバックを得る
テストの開始や終了のコールバックを得るにはTestRunCallback
アトリビュートを使います。
using NUnit.Framework.Interfaces; using UnityEngine; using UnityEngine.TestRunner; [assembly:TestRunCallback(typeof(TestCallback))] public class TestCallback : ITestRunCallback { public void RunStarted(ITest testsToRun) { Debug.Log("テスト開始"); } public void RunFinished(ITestResult testResults) { Debug.Log("テスト終了"); } public void TestStarted(ITest test) { if (!test.IsSuite) { Debug.Log($"個々のテスト開始 : {test.Name}"); } } public void TestFinished(ITestResult result) { if (!result.Test.IsSuite) { Debug.Log($"個々のテスト終了 : {result.Test.Name} / {result.ResultState.Status}"); } } }
テスト用のPrimitiveのGameObjectを作る
UnityEngine.TestTools.Utils.Utils.CreatePrimitive()
を使うとテスト用のプリミティブのGameObjectを作成できます。
挙動としては、GameObject.CreatePrimitive()
と同様ですが、テスト用に軽いシェーダがアサインされます。
よってGameObject.CreatePrimitive()
よりもテスト時のパフォーマンスがよくなります。