【C#】xUnit.netでのテスト前処理・後処理(コンストラクタとDispose)

目次

概要

xUnit.netでは、テストメソッドを実行するたびにテストクラスのインスタンスが新しく生成され、終了時に破棄されます。 このライフサイクルを利用して、コンストラクタに「前処理(Setup)」を、IDisposable インターフェースの Dispose メソッドに「後処理(Teardown)」を記述するのが標準的な作法です。

仕様(ライフサイクル)

  • 前処理: コンストラクタ(public ClassName() { ... })に記述。テストメソッドごとに毎回呼ばれます。
  • 後処理: Dispose() メソッドに記述。テストメソッド終了後に毎回呼ばれます。
  • インターフェース: IDisposable を実装する必要があります。

基本の使い方

public class MyTests : IDisposable
{
    // コンストラクタ:前処理
    public MyTests()
    {
        // データベース接続やリストの初期化など
    }

    // Dispose:後処理
    public void Dispose()
    {
        // ファイルの削除や接続の切断など
    }

    [Fact]
    public void Test1() { /* ... */ }
}

コード全文

スタック(Stack<T>)の初期化を前処理として共通化し、各テストメソッドでクリーンな状態のインスタンスを利用する例です。

using System;
using System.Collections.Generic;
using Xunit;

namespace UnitTestPatterns
{
    // IDisposableを実装して後処理を行えるようにする
    public class StackTests : IDisposable
    {
        private Stack<int> _stack;

        /// <summary>
        /// 【前処理】
        /// コンストラクタはテストメソッド(Test1, Test2)が実行されるたびに
        /// 毎回呼び出され、新しいインスタンス(_stack)が生成されます。
        /// </summary>
        public StackTests()
        {
            _stack = new Stack<int>();
            Console.WriteLine("Setup: Stack initialized.");
        }

        /// <summary>
        /// 【後処理】
        /// テストメソッドの実行が終わるたびに呼び出されます。
        /// リソースの解放などが必要な場合に記述します。
        /// </summary>
        public void Dispose()
        {
            _stack = null; // 不要ですが例として
            Console.WriteLine("Teardown: Resources released.");
        }

        [Fact]
        public void Push_OneItem_CountIsOne()
        {
            // Arrange (コンストラクタで完了済み)

            // Act
            _stack.Push(42);
            var count = _stack.Count;

            // Assert
            Assert.Equal(1, count);
        }

        [Fact]
        public void Pop_OneItem_ReturnsValueAndCountIsZero()
        {
            // Arrange
            _stack.Push(11);

            // Act
            var val = _stack.Pop();
            var count = _stack.Count;

            // Assert
            Assert.Equal(11, val); // 取り出した値は11のはず
            Assert.Equal(0, count); // 空になっているはず
        }
    }
}

カスタムポイント

  • クラスフィクスチャ(IClassFixture): コンストラクタによる前処理は「テストメソッドごと」に実行されますが、データベースの接続など「テストクラス全体で1回だけ」行いたい重い処理には IClassFixture<T> を使用します。
  • コレクションフィクスチャ: 複数のテストクラス間でコンテキストを共有したい場合は [Collection] 属性を使用します。

注意点

  1. インスタンスは使い捨て: xUnitはテストメソッドごとにクラスを new します。Test1_stack に加えた変更は、Test2 実行時の _stack には影響しません(常に空の状態から始まります)。これを理解していないと「なぜ前のテストのデータが消えているのか?」と混乱します。
  2. Disposeの必要性: メモリ上の単純なオブジェクト(ListStack など)を扱うだけであれば、ガベージコレクションが処理してくれるため、明示的な Dispose の実装は不要です。ファイルハンドルやDB接続など、アンマネージドリソースを扱う場合のみ実装してください。

応用

非同期の初期化・終了処理

非同期処理(async/await)が必要な初期化を行いたい場合は、IAsyncLifetime インターフェースを実装します。

public class AsyncTests : IAsyncLifetime
{
    public async Task InitializeAsync()
    {
        // 非同期セットアップ(例:DBへのデータ投入)
        await PrepareDatabaseAsync();
    }

    public async Task DisposeAsync()
    {
        // 非同期クリーンアップ
        await CleanDatabaseAsync();
    }
    
    // ... [Fact] メソッド ...
}

まとめ

xUnit.netにおいて、テストの前処理はコンストラクタ、後処理は Dispose メソッドに記述します。この「メソッドごとに環境を作り直す」という設計思想により、テスト間の依存関係(副作用)が排除され、どのテストから実行しても同じ結果になることが保証されます。

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

この記事を書いた人

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

目次