【C#】xUnit.netで特定の例外が発生するか検証する

目次

概要

メソッド実行時に期待した通りの例外(エラー)が発生するかをテストする実装です。 xUnit.netの Assert.Throws<T> メソッドを使用すると、内部の処理で指定した例外がスローされればテスト成功、スローされなければ(または違う例外が出れば)テスト失敗と判定されます。 引数チェックや異常系処理のロジックを検証する際に不可欠な機能です。

仕様(入出力)

  • 入力: 例外が発生すると予想される処理(Actionデリゲート)
  • 出力: 発生した例外オブジェクト(T 型)。これを使ってさらにメッセージ内容などを検証可能。
  • 判定基準:
    • 指定した型 T の例外が発生した → 成功
    • 例外が発生しなかった → 失敗
    • 指定した型とは異なる例外が発生した → 失敗

基本の使い方

// ArgumentNullExceptionが発生することを期待
Assert.Throws<ArgumentNullException>(() => 
{
    // 例外が出るはずの処理
    target.DoSomething(null); 
});

コード全文

例外の発生有無だけでなく、返ってきた例外オブジェクトを使ってパラメータ名まで検証する完全な例です。

using System;
using Xunit;

namespace ExceptionTesting
{
    public class ExceptionTests
    {
        [Fact]
        public void TestMethod_NullInput_ThrowsArgumentNullException()
        {
            var obj = new MySampleClass();

            // Act & Assert
            // 1. ArgumentNullExceptionが発生することを検証
            var ex = Assert.Throws<ArgumentNullException>(() =>
            {
                obj.TestMethod(null);
            });

            // 2. 発生した例外の中身をさらに検証(任意)
            // どの引数が原因でエラーになったかを確認できる
            Assert.Equal("inputObject", ex.ParamName);
        }

        [Fact]
        public void TestMethod_ValidInput_NoException()
        {
            var obj = new MySampleClass();

            // 正常系の動作確認(例外が出ないことの確認)
            // 例外が出なければテストは自動的にパスします
            obj.TestMethod(new object());
        }
    }

    /// <summary>
    /// テスト対象クラス
    /// </summary>
    public class MySampleClass
    {
        public void TestMethod(object inputObject)
        {
            if (inputObject == null)
            {
                // 引数名を指定して例外をスロー
                throw new ArgumentNullException(nameof(inputObject));
            }
            
            // 正常な処理...
        }
    }
}

カスタムポイント

  • 派生クラスの許容: 継承関係にある例外も含めて許可したい場合は、Assert.ThrowsAny<T> を使用します。 例: Assert.ThrowsAny<Exception>(() => ...) とすれば、どんな例外でも発生すればOKとなります。
  • 例外メッセージの検証: 戻り値の ex を使うことで、エラーメッセージが適切かどうかもテストできます。C#var ex = Assert.Throws<InvalidOperationException>(...); Assert.Equal("処理を実行できません。", ex.Message);

注意点

  1. 範囲を最小限にする: Assert.Throws のラムダ式の中には、例外が発生する「その1行だけ」を書くのがベストプラクティスです。範囲を広げすぎると、予期しない別の場所での例外(セットアップ時のエラーなど)を誤って「テスト成功」と判定してしまうリスクがあります。
  2. 例外が出ない場合: Assert.Throws は「例外が出ること」が正解なので、処理が正常終了してしまうとテストは失敗(Fail)します。
  3. 型の一致: Assert.Throws<ArgumentException> は、派生クラスである ArgumentNullException を捕捉しません(厳密な型一致が必要です)。継承関係を考慮する場合は ThrowsAny を使ってください。

応用

非同期メソッドの例外テスト

async/await を使用するメソッドの場合は、Assert.ThrowsAsync を使用します。

[Fact]
public async Task TestAsyncMethod_ThrowsException()
{
    var obj = new MySampleClass();

    // ThrowsAsyncを使用し、awaitで待機する
    await Assert.ThrowsAsync<InvalidOperationException>(async () =>
    {
        await obj.RunAsync(null);
    });
}

まとめ

Assert.Throws<T> は異常系の単体テストにおける要です。「何が起きるべきか」だけでなく「何が起きてはいけないか(不正な引数で処理が進んでしまわないか)」を保証するために、nullチェックやバリデーションロジックに対して必ず実装するようにします。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次