はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【Unity】Unity Test Frameworkで特定の例外がスローされることをAssertする方法(Assert.Throws, Assert.Catch, Throws.TypeOf, Throws.InstanceOf)

はじめに

今回はUnity Test Frameworkでエラーのテストをする方法について紹介したいと思います。

docs.nunit.org

Assert.Throws

Assert.Throwsを利用することで特定の例外がスローされることをチェックすることができます。

The Assert.Throws method is pretty much in a class by itself. Rather than comparing values, it attempts to invoke a code snippet, represented as a delegate, in order to verify that it throws a particular exception.

It's also in a class by itself in that it returns an Exception, rather than void, if the Assert is successful. See the example below for a few ways to use this.

// DeepL翻訳
Assert.Throwsメソッドは、それだけで一つのクラスになっている。値を比較するのではなく、デリゲートとして表現されたコード・スニペットを呼び出して、それが特定の例外をスローするかどうかを検証しようとします。

また、Assert が成功した場合は void ではなく Exception を返すという点でも、それ自体がひとつのクラスになっています。これを使用するいくつかの方法については、以下の例を参照してください。

Assert.Throws | NUnit Docs

基本的な使い方は以下の通り。

[Test]
public static void Hoge()
{
    // ジェネリックを利用したタイプ(個人的にこっちの方が使いやすいと思う)
    Assert.Throws<NotImplementedException>(() =>
    {
        // NotImplementedExceptionをスローするコードが記述されていればPass, それ以外はFail
        throw new NotImplementedException();
    });
        
    // ジェネリックを利用しないタイプ
    var exception = Assert.Throws(typeof(NotImplementedException), () =>
    {
        // NotImplementedExceptionをスローするコードが記述されていればPass, それ以外はFail
        throw new NotImplementedException();
    });
        
    // System.NotImplementedException
    Debug.Log(exception.GetType());
}

また非同期用のAssert.ThrowAsyncが用意されているのですが、現状のUnityですと正しく動作しないので注意です。

Assert.ThrowsAsync<NotImplementedException>(async () =>
{
    // NotImplementedExceptionをスローするコードが記述されていればPass, それ以外はFail
    await Task.Delay(1);
    throw new NotImplementedException();
});

www.nowsprinting.com

Assert.Catch

Assert.Throwsの場合は指定した例外の型が同じでないとダメでしたが、Assert.Catchを利用すると派生型も許容するようになります。

// 派生方も許容する場合はCatchを利用する, ジェネリックを利用したタイプ
Assert.Catch<Exception>(() =>
{
    // TActual型(ここではException)+その派生型をスローするコードが記述されていればPass, それ以外はFail
    throw new NotImplementedException();
});

var exception2 = Assert.Catch(typeof(Exception), () =>
{
    // TActual型(ここではException)+その派生型をスローするコードが記述されていればPass, それ以外はFail
    throw new NotImplementedException();
});

// System.NotImplementedException
Debug.Log(exception2);

Assert.Thatを利用する

上記の2種類で事足りるのですが、Assert.Thatをなるべく使って欲しいという方もいらっしゃるのでAssert.Thatを利用した方法も紹介します。

// Assert.ThrowsのAssert.Thatを利用したバージョン
Assert.That(() =>
{
    // NotImplementedExceptionをスローするコードが記述されていればPass, それ以外はFail
    throw new NotImplementedException();
}, Throws.TypeOf<NotImplementedException>());
        
// Assert.CatchのAssert.Thatを利用したバージョン
Assert.That(() =>
{
    // NotImplementedExceptionをスローするコードが記述されていればPass, それ以外はFail
    throw new NotImplementedException();
}, Throws.InstanceOf<Exception>());

小ネタ

特定の型の例外かどうかを調べるだけでなく、メッセージが等しいかなどの制約を加えたりもできます。

Assert.That(() =>
    {
        // NotImplementedExceptionをスローするコードが記述されていればPass, それ以外はFail
        throw new NotImplementedException("Hoge");
    },
    Throws.TypeOf<NotImplementedException>().And.Message.EqualTo("Hoge")
);