白黒羊

【Unity】Unit Testでハマったところメモ

リファクタリング後に不安になって眠れない日々はこりごりなので、最近はもっぱらUnit Testを書いています。
基本的なUnit Testの書き方は公式マニュアルや先人たちの記事を読めば大体わかるのですが、軽く躓いた部分をメモしました。

[SetUp]や[TearDown]が呼ばれない

アクセス修飾子が public になっているか確認しましょう。
[OneTimeSetUp] はそのクラスのテストで1回だけ、 [SetUp] はテストケースごとに呼ばれます。

[SetUp]
public void SetUp()
{
    _foo = new Foo();
}

Unit Test中にシーンを呼び出したい

PlayMode テストを使いましょう。読み込みには時間がかかるので、yield return new WaitForSeconds(1); などで待つ必要があります。

参考

【Unity】謎のInitTestSceneが生成される – エンジニア戦記

MonoBehaviourのテストをしたい

以下のページを参考にMonoBehaviourのテストを書くことができました。

public class TestBehaviour : MonoBehaviour, IMonoBehaviourTest
{
    public bool IsTestFinished { get; private set; }

    [UsedImplicitly] private void Awake()
    {
        _uiDocument = gameObject.AddComponent<UIDocument>();
        _uiDocument.rootVisualElement.Add(new Box { name = "canvas" });
        _waitScreen = gameObject.AddComponent<WaitScreen>();
    }

    [UsedImplicitly] private void Start()
    {
        IsTestFinished = true;
    }
}

参考

MonoBehaviour tests | Test Framework | 1.1.31

【Unity】Unity Test Runner(Test Framework)の小技色々まとめ – LIGHT11

初回のみNull Pointer Exceptionが出て失敗する

かなり特殊状況だと思います。他の人がハマるかはわかりませんが、自分がもう一度ハマるかもしれないのでメモ。

症状

シーンを入れ替えるテストが失敗したのですが、その失敗したテストを再度走らせると今度は成功しました。

コード

private void Awake()
{
  var uiDocument = GetComponent<UIDocument>();

UIDocumentを持っているシーンを読み込んでいるはずなのに、初回のみ上記のGetComponentが失敗してしまいます。これが Awake ではなく Startだと成功するので、初回実行時はUIDocumentが作成されるのが少し遅くなってしまうようです。

エラーメッセージ

[Exception] NullReferenceException: Object reference not set to an instance of an object'. Use UnityEngine.TestTools.LogAssert.Expect]]></message>
<stack-trace><![CDATA[Project.Scripts.View.WaitScreen.Start () (at Assets/Project/Scripts/View/WaitScreen.cs:18)

解決方法

上記のAwakeはテストコードではなくRuntimeに使っているコードだったのであまり手を加えたくなかったため、その部分はそのままで何とかなる方法を考えます。
シーンに含まれているComponentの初期化が間に合わないので、そのシーンから読み取るのを諦めて、 IMonoBehaviourTest を継承したクラスで手動でAddComponentしました。

private static WaitScreen _waitScreen;

public class TestBehaviour : MonoBehaviour, IMonoBehaviourTest
{
    public bool IsTestFinished { get; private set; }

    private void Awake()
    {
        var uiDocument = gameObject.AddComponent<UIDocument>();
        uiDocument.rootVisualElement.Add(new Box { name = "canvas" });
        _waitScreen = gameObject.AddComponent<WaitScreen>();
    }

    private void Start()
    {
        IsTestFinished = true;
    }
}

参考

NullReferenceException only at first run – Unity Answers