Cooooding!!

Unity(C#)を使ったゲーム開発関連Tipsなど

TestTools.LogAssertについて調べてみた【Unity】

概要

Test Runner(NUnit)でログが出力されることをテストできるTestTools.LogAssertを使おうと思ったのですが、ドキュメントを呼んでもよくわからなかったので詳しく調べてみました。

LogAssertの基礎

LogAssertにはExpectとNoUnexpectedReceivedの2つの関数があります。

1.Expectについて

LogAssert.Expectは指定したログが出力されなければエラーにする関数です。

例えば、↓これはエラーになりませんが

[Test]
public void Expect_Log()
{
    LogAssert.Expect(LogType.Log, "abc");
    Debug.Log("abc");
}

↓これはエラーになります。

[Test]
public void Expect_Log_Fail()
{
    LogAssert.Expect(LogType.Log, "abc");
    // 期待していたログが出力されない
}

エラー内容

Expected log did not appear: [Log] abc

上記の例ではLog関数を使っていますが、LogWarningやLogErrorでも同様です。

[Test]
public void Expect_Error()
{
    LogAssert.Expect(LogType.Error, "xxxx");
    Debug.LogError("xxxx");
}

エラーログが出力されていますがExpectされたものなのでテストを通ります。

ログの内容は正規表現で指定することもできます。

[Test]
public void Expect_Regex()
{
    LogAssert.Expect(LogType.Log, new Regex("[0-9]+"));
    Debug.Log("123456789");
}

2.NoUnexpectedReceivedについて

LogAssert.NoUnexpectedReceivedはExpectされてないログが出力されていたらエラーにする関数です。

例えば、↓これはエラーになりますが

[Test]
public void NoUnexpectedReceived_Fail()
{
    Debug.Log("xxxx");
    LogAssert.NoUnexpectedReceived();  // Expectされてないログが出力されたのでエラー
}

↓これはエラーになりません。

[Test]
public void NoUnexpectedReceived()
{
    LogAssert.Expect(LogType.Log, "xxxx");
    Debug.Log("xxxx");
    LogAssert.NoUnexpectedReceived();  // Expectされたログ以外は出力されなかったのでOK
}

詳細な挙動

ここからはもっと詳細な挙動を見てみます。

1.一度Expectされたログを何度出力していいわけではない

以下のようにExpectしたログを2回出力するとNoUnexpectedReceivedでエラーになります。

[Test]
public void LogRepeat_Fail()
{
    LogAssert.Expect(LogType.Log, "xxxx");
    Debug.Log("xxxx");
    Debug.Log("xxxx");
    LogAssert.NoUnexpectedReceived();
}

以下のように2回Expectしておけばエラーになりません。

[Test]
public void LogRepeat2()
{
    LogAssert.Expect(LogType.Log, "xxxx");
    LogAssert.Expect(LogType.Log, "xxxx");
    Debug.Log("xxxx");
    Debug.Log("xxxx");
    LogAssert.NoUnexpectedReceived();
}

2.複数回Expectされた場合は登録順にマッチする

挙動を観察した限りでは 複数回ExpectするとExpectされた順にマッチするようです。マッチする順序を考慮しないと意図せずエラーになってしまうことがあるかもしれません。

[Test]
public void MultiExpectOrder_Fail()
{
    LogAssert.Expect(LogType.Log, new Regex("[a-z]+"));  // xxxxとマッチさせるつもりが…
    LogAssert.Expect(LogType.Log, "yy");
    Debug.Log("yy");      // 1つ目のExpectとマッチしてしまう
    Debug.Log("xxxx");
    LogAssert.NoUnexpectedReceived();  // xxxxがExpectされていないログになりエラー
}

3.Expectした直後に出力される必要はない

Expectしたログはすぐに出力される必要はなく、NoUnexpectedReceivedの後だったり別のログが出力された後でも問題ありません。例えば、以下のテストも通ります。

[Test]
public void ExpectAfter()
{
    LogAssert.Expect(LogType.Log, "xxxx");
    Test0();    // この関数でxxxxが出力されることを期待していたが…
    LogAssert.NoUnexpectedReceived();

    Test1();    // この関数でxxxxが出力されてしまったのでテストが通ってしまった
}

public void Test0() { }
public void Test1() { Debug.Log("yyy"); Debug.Log("xxxx"); }

4.ログの出力をしてからExpectしてもテストが通る

実はログを出力してからExpectしてもテストが通ります。

[Test]
public void ExpectOrder()
{
    Debug.Log("xxxx");
    LogAssert.Expect(LogType.Log, "xxxx");
}

感想

順序を入れ替えても動く挙動が直感的でなかったり、Expectしたものをクリアできなかったりして使いにくい印象です。特に1つのテスト関数で複数の関数をテストする場合には注意が必要そうです。ログのテストをするなら1つのテスト関数でテスト対象の関数を1回だけ呼び出すようにするのが良さそうです。

環境

  • Unity 2019.1.9f1
  • VisualStudio 2019