やらなイカ?

たぶん、iOS/Androidアプリの開発・テスト関係。

Test Helper パッケージ v1.0.0 リリース

Unity Test Framework でテストを書くときに便利なライブラリ Test Helper パッケージのバージョン 1.0.0 をリリースしました*1

github.com

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.
    }
}

バッチモードの制限事項と回避方法について詳しくは過去記事を参照してください。

www.nowsprinting.com

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.
    }
}

カスタム属性の実装方法については過去記事を参照してください。

www.nowsprinting.com

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形式のテストレポートファイルを出力できます。

宣伝

www.nowsprinting.com

www.nowsprinting.com

*1:v0.1.0 の公開が May 20, 2023 なので、実に1年半

*2:詳しくはリポジトリのREADMEを参照してください

*3:公式な用語ではありません。非バッチモードの意味です