Unity Test Framework でテストを書くときに便利なライブラリ Test Helper パッケージのバージョン 1.0.0 をリリースしました*1。
Test Helperパッケージは、Unity Test FrameworkおよびNUnit 3の属性(attribute)、Comparer、制約(constraint)などの汎用的な拡張を詰め合わせたものです。 拡張のサンプルとしてもご利用いただけるはずです。
インストールは、GitHubリポジトリ指定や、openupm.comから可能です。 openupm-cli であれば次のコマンドでインストールできます。
openupm add com.nowsprinting.test-helper
以下、v1.0.0で実装している機能を簡単に紹介します*2。
属性(attribute)
FocusGameView
この属性をテストに付与すると、テスト実行時にGameViewにフォーカスが移ります。
フォーカスが無いと正しく動作しないテストケースに有効なほか、バッチモードでもGameViewを表示することで、バッチモードの様々な制限事項(WaitForEndOfFrame
を使用できない等)を回避できます。
次のように使用します。
[TestFixture] public class MyTestClass { [Test] [FocusGameView] public void MyTestMethod() { // e.g., Test using InputEventTrace of Input System package. } }
バッチモードの制限事項と回避方法について詳しくは過去記事を参照してください。
GameViewResolution
この属性では、テスト実行時のGameView解像度を指定できます。
また FocusGameView
属性と同様、バッチモードでもGameViewを表示できます。
次のように使用します。
[TestFixture] public class MyTestClass { [UnityTest] [GameViewResolution(640, 480, "VGA")] public IEnumerator MyTestMethod() { yield return null; // Wait for one frame to apply resolution. // e.g., Test using GraphicRaycaster. } }
GizmosShowOnGameView
この属性を付与したテストを実行する間、GameViewにGizmoを表示できます。
次のように使用します。
[TestFixture] public class MyTestClass { [Test] [GizmosShowOnGameView(true)] public void MyTestMethod() { // Show Gizmos on GameView during the test running. } }
IgnoreBatchMode
この属性を付与したテストは、バッチモードでスキップされます。
次のように使用します。
[TestFixture] public class MyTestClass { [UnityTest] [IgnoreBatchMode("Using WaitForEndOfFrame.")] public IEnumerator MyTestMethod() { // e.g., Test needs to take a screenshot. yield return new WaitForEndOfFrame(); ImageAssert.AreEqual(expectedTexture, Camera.main, settings); } }
IgnoreWindowMode
IgnoreBatchMode
属性と逆に、ウィンドウモード*3でスキップされます。
次のように使用します。
[TestFixture] public class MyTestClass { [Test] [IgnoreWindowMode("Requires command line arguments")] public void MyTestMethod() { var args = Environment.GetCommandLineArgs(); Assert.That(args, Does.Contain("-arg1")); } }
UnityVersion
テストを実行するUnityエディターのバージョンによってテストをスキップできます。
#if
ディレクティブでも実現できますが、この属性を使うことによって明示的にスキップしたテストをテスト実行結果に残すことができます。
次のように使用します。
[TestFixture] public class MyTestClass { [Test] [UnityVersion(newerThanOrEqual: "2022")] public void MyTestMethod() { // Test run only for Unity 2022.1.0f1 or later. } [Test] [UnityVersion(olderThan: "2019.4.0f1")] public void MyTestMethod() { // Test run only for Unity older than 2019.4.0f1. } }
CreateScene
テスト実行時に空のSceneを生成して使用します。
次のように使用します。
[TestFixture] public class MyTestClass { [Test] [CreateScene(camera: true, light: true)] public void MyTestMethod() { var camera = GameObject.Find("Main Camera"); Assert.That(camera, Is.Not.Null); var light = GameObject.Find("Directional Light"); Assert.That(light, Is.Not.Null); } }
LoadScene
テスト実行時に指定したSceneファイルをロードします。 通常、Edit Modeテスト、Play ModeテストのUnityエディター内実行、プレイヤー実行でSceneの指定方法は異なり、またScenes in Buildに含まれないSceneファイルはプレイヤー実行で使用できない制約がありますが、この属性を使用すればこれらはすべて考えなくてよくなります。
次のように使用します。
[TestFixture] public class MyTestClass { [Test] [LoadScene("Assets/MyTests/Scenes/TestScene.unity")] public void MyTestMethod() { var cube = GameObject.Find("Cube in TestScene"); Assert.That(cube, Is.Not.Null); } [Test] [LoadScene("Packages/YourPackageName/**/SampleScene.unity")] public void UsingGlobPattern() { // snip } [Test] [LoadScene("../../Scenes/SampleScene.unity")] public void UsingRelativePath() { // snip } }
BuildScene
プレイヤーでも実行するテストにおいて(かつLoadScene
属性が使えないケースで)Scenes in Buildに含まれないSceneをビルドに含めるためのマーカー属性です。
次のように使用します。
Usage:
[TestFixture] public class MyTestClass { [Test] [BuildScene("../../Scenes/SampleScene.unity")] public void MyTestMethod() { // Setup before load scene // Load scene await SceneManagerHelper.LoadSceneAsync("../../Scenes/SampleScene.unity"); // Excercise the test } }
TakeScreenshot
この属性を付与したテストの実行終了時、スクリーンショットを撮影します。
次のように使用します。
[TestFixture] public class MyTestClass { [Test] [TakeScreenshot] public void MyTestMethod() { // Take screenshot after running the test. } }
TimeScale
この属性を付与したテストを実行する間、Time.timeScale を変更します。
次のように使用します。
[TestFixture] public class MyTestClass { [Test] [TimeScale(2.0f)] public void MyTestMethod() { // Running at 2x speed. } }
カスタム属性の実装方法については過去記事を参照してください。
Comparer
Comparerは、NUnit 3の制約モデル(Assert.That
)で検証するときに比較方法をUsing
修飾子で指定するものです。
GameObjectNameComparer
GameObject同士をname
で比較するComparerです。
次のように使用します。
[TestFixture] public class MyTestClass { [Test] public void MyTestMethod() { var actual = GameObject.FindObjectsOfType<GameObject>(); Assert.That(actual, Does.Contain(new GameObject("test")).Using(new GameObjectNameComparer())); } }
XDocumentComparer
XDocument
同士をゆるく比較するComparerです。要素の順序やコメントを無視して比較できます。
次のように使用します。
[TestFixture] public class MyTestClass { [Test] public void MyTestMethod() { var x = XDocument.Parse(@"<root><child>value1</child><child attribute=""attr"">value2</child></root>"); var y = XDocument.Parse(@"<?xml version=""1.0"" encoding=""utf-8""?> <root><!-- comment --><child attribute=""attr"">value2</child><!-- comment --><child>value1</child></root>"); // with XML declaration, comments, and different order Assert.That(x, Is.EqualTo(y).Using(new XDocumentComparer())); } }
XmlComparer
string
同士を、XMLとしてゆるく比較するComparerです。改行やスペース、要素の順序やコメントを無視して比較できます。
次のように使用します。
[TestFixture] public class MyTestClass { [Test] public void MyTestMethod() { var x = @"<root><child>value1</child><child attribute=""attr"">value2</child></root>"; var y = @"<?xml version=""1.0"" encoding=""utf-8""?> <root> <!-- comment --> <child attribute=""attr""> value2 </child> <!-- comment --> <child> value1 </child> </root>"; Assert.That(x, Is.EqualTo(y).Using(new XmlComparer())); } }
制約(constraint)
NUnit 3の制約モデル(Assert.That)で使用できる制約です。
Destroyed
GameObjectが(nullでなく)破棄されているかを検証します。
次のように使用します。
using Is = TestHelper.Constraints.Is; [TestFixture] public class MyTestClass { [Test] public void MyTestMethod() { var actual = GameObject.Find("Cube"); GameObject.DestroyImmediate(actual); Assert.That(actual, Is.Destroyed); } }
ランライムAPI
上記カスタム属性の機能のうちいくつかは、APIで呼び出せるようになっています。
使用するには、TestHelper.RuntimeInternals
アセンブリをAssembly Definition Referencesに追加する必要があります。
ScreenshotHelper
テストコード内の任意の場所でスクリーンショットを撮影するAPIです。 次のように使用します。
[TestFixture] public class MyTestClass { [UnityTest] public IEnumerator MyTestMethod() { yield return ScreenshotHelper.TakeScreenshot(); } [Test] public async Task MyTestMethodAsync() { var coroutineRunner = new GameObject().AddComponent<CoroutineRunner>(); await ScreenshotHelper.TakeScreenshot().ToUniTask(coroutineRunner); } private class CoroutineRunner : MonoBehaviour { } }
SceneManagerHelper
テストコード内の任意の場所でSceneをロードするAPIです。 Edit Modeテスト、Play ModeテストのUnityエディター内実行、プレイヤー実行の区別なく実行できます。 次のように使用します。
[TestFixture] public class MyTestClass { [Test] public void MyTestMethod() { // Setup before load scene // Load scene await SceneManagerHelper.LoadSceneAsync("../../Scenes/SampleScene.unity"); // Excercise the test } }
エディター拡張
Window > Test Helper > Open Persistent Data Directory
Application.persistentDataPath ディレクトリを Finder/ File Explorer で開きます。
TakeScreenshot
属性のデフォルト保存先はApplication.persistentDataPath
下になっています。
Window > Test Helper > Temporary Cache Directory
Application.temporaryCachePath ディレクトリを Finder/ File Explorer で開きます。
テストコードから使用する一時ファイルの保存先に使うことが多いため追加しました。
JUnit XML format report
コマンドライン引数に -testHelperJUnitResults
を指定することで、テスト終了時に(NUnit 3でなく)JUnit XML形式のテストレポートファイルを出力できます。