次の方法で共有


MSTest ライフサイクル

MSTest には、テスト クラスとテスト メソッドの明確に定義されたライフサイクルが用意されており、テスト実行のさまざまな段階でセットアップ操作と破棄操作を実行できます。 ライフサイクルを理解することは、効率的なテストを記述し、一般的な落とし穴を回避するのに役立ちます。

ライフサイクルの概要

ライフサイクルは、最高レベル (アセンブリ) から最下位レベル (テスト メソッド) に実行される 4 つのステージにグループ化されます。

  1. アセンブリ レベル: テスト アセンブリの読み込みとアンロード時に 1 回実行されます
  2. クラス レベル: テスト クラスごとに 1 回実行
  3. グローバル テスト レベル: アセンブリ内のすべてのテスト メソッドの前後に実行されます
  4. テスト レベル: 各テスト メソッドに対して実行されます (パラメーター化されたテストの各データ行を含む)

アセンブリ レベルのライフサイクル

アセンブリ ライフサイクル メソッドは、テスト アセンブリの読み込みとアンロード時に 1 回実行されます。 データベースの初期化やサービスの起動など、コストの高い 1 回限りのセットアップに使用します。

AssemblyInitialize および AssemblyCleanup

[TestClass]
public class AssemblyLifecycleExample
{
    private static IHost? _host;

    [AssemblyInitialize]
    public static async Task AssemblyInit(TestContext context)
    {
        // Runs once before any tests in the assembly
        _host = await StartTestServerAsync();
        context.WriteLine("Test server started");
    }

    [AssemblyCleanup]
    public static async Task AssemblyCleanup(TestContext context)
    {
        // Runs once after all tests complete
        // TestContext parameter available in MSTest 3.8+
        if (_host != null)
        {
            await _host.StopAsync();
        }
    }

    private static Task<IHost> StartTestServerAsync()
    {
        // Server initialization
        return Task.FromResult<IHost>(null!);
    }
}

Requirements

  • メソッドは〘 public static
  • 戻り値の型: voidTask、または ValueTask (MSTest v3.3 以降)
  • AssemblyInitialize には、1 つの TestContext パラメーターが必要です
  • AssemblyCleanup 0 個のパラメーターまたは 1 つの TestContext パラメーターを受け取ります (MSTest 3.8 以降)
  • アセンブリごとに許可される各属性の 1 つだけ
  • でマークされたクラスに存在する必要があります [TestClass]

ヒント

関連するアナライザー:

  • MSTEST0012 - 署名 AssemblyInitialize 検証します。
  • MSTEST0013 - 署名 AssemblyCleanup 検証します。

クラス レベルのライフサイクル

クラス ライフサイクル メソッドは、テスト クラスごとに、そのクラスのすべてのテスト メソッドの前後に 1 回実行されます。 これらは、クラス内のテスト間で共有されるセットアップに使用します。

ClassInitialize および ClassCleanup

[TestClass]
public class ClassLifecycleExample
{
    private static HttpClient? _client;

    [ClassInitialize]
    public static void ClassInit(TestContext context)
    {
        // Runs once before any tests in this class
        _client = new HttpClient
        {
            BaseAddress = new Uri("https://api.example.com")
        };
    }

    [ClassCleanup]
    public static void ClassCleanup()
    {
        // Runs after all tests in this class complete
        _client?.Dispose();
    }

    [TestMethod]
    public async Task GetUsers_ReturnsSuccess()
    {
        HttpResponseMessage response = await _client!.GetAsync("/users");
        Assert.IsTrue(response.IsSuccessStatusCode);
    }
}

Requirements

  • メソッドは〘 public static
  • 戻り値の型: voidTask、または ValueTask (MSTest v3.3 以降)
  • ClassInitialize には、1 つの TestContext パラメーターが必要です
  • ClassCleanup 0 個のパラメーターまたは 1 つの TestContext パラメーターを受け取ります (MSTest 3.8 以降)
  • クラスごとに許可される各属性の 1 つだけ

継承の振る舞い

ClassInitializeを使って、派生クラスがInheritanceBehaviorを実行するかどうかを制御します。

[TestClass]
public class BaseTestClass
{
    [ClassInitialize(InheritanceBehavior.BeforeEachDerivedClass)]
    public static void BaseClassInit(TestContext context)
    {
        // Runs before each derived class's tests
    }
}

[TestClass]
public class DerivedTestClass : BaseTestClass
{
    [TestMethod]
    public void DerivedTest()
    {
        // BaseClassInit runs before this class's tests
    }
}
InheritanceBehavior Description
None (既定値) 宣言するクラスに対してのみ実行を初期化する
BeforeEachDerivedClass 各派生クラスの前に実行を初期化する

ヒント

関連するアナライザー:

  • MSTEST0010 - 署名 ClassInitialize 検証します。
  • MSTEST0011 - 署名 ClassCleanup 検証します。
  • MSTEST0034 - ClassCleanupBehavior.EndOfClassの使用をお勧めします。

グローバルテストレベルライフサイクル

グローバル テスト ライフサイクル属性は、MSTest 3.10.0 で導入されました。

グローバル テスト ライフサイクル メソッドは、各テスト クラスにコードを追加する必要なく、アセンブリ全体 のすべての テスト メソッドの前後に実行されます。

GlobalTestInitialize および GlobalTestCleanup

[TestClass]
public class GlobalTestLifecycleExample
{
    [GlobalTestInitialize]
    public static void GlobalTestInit(TestContext context)
    {
        // Runs before every test method in the assembly
        context.WriteLine($"Starting test: {context.TestName}");
    }

    [GlobalTestCleanup]
    public static void GlobalTestCleanup(TestContext context)
    {
        // Runs after every test method in the assembly
        context.WriteLine($"Finished test: {context.TestName}");
    }
}

Requirements

  • メソッドは〘 public static
  • 戻り値の型: voidTask、または ValueTask
  • TestContext パラメーターは正確に 1 つである必要があります。
  • でマークされたクラスに存在する必要があります [TestClass]
  • これらの属性を持つ複数のメソッドがアセンブリ全体で許可される

複数の GlobalTestInitialize または GlobalTestCleanup メソッドが存在する場合、実行順序は保証されません。 TimeoutAttributeは、GlobalTestInitializeメソッドではサポートされていません。

ヒント

関連アナライザー: MSTEST0050 - グローバル テスト フィクスチャ メソッドを検証します。

試験段階のライフサイクル

テスト レベルのライフサイクルは、すべてのテスト メソッドに対して実行されます。 パラメーター化されたテストの場合、ライフサイクルはデータ行ごとに実行されます。

セットアップ フェーズ

テストごとのセットアップには、 TestInitialize またはコンストラクターを使用します。

[TestClass]
public class TestLevelSetupExample
{
    private Calculator? _calculator;

    public TestLevelSetupExample()
    {
        // Constructor runs before TestInitialize
        // Use for simple synchronous initialization
    }

    [TestInitialize]
    public async Task TestInit()
    {
        // Runs before each test method
        // Supports async, attributes like Timeout
        _calculator = new Calculator();
        await _calculator.InitializeAsync();
    }

    [TestMethod]
    public void Add_TwoNumbers_ReturnsSum()
    {
        int result = _calculator!.Add(2, 3);
        Assert.AreEqual(5, result);
    }
}

コンストラクターと TestInitialize:

特徴 コンストラクター TestInitialize
非同期サポート いいえ イエス
タイムアウトのサポート いいえ はい ( [Timeout] 属性を使用)
実行順序 First コンストラクタ処理後
継承 次に派生した Base 次に派生した Base
例外の動作 クリーンアップと Dispose が実行されない (インスタンスが存在しない) クリーンアップとDisposeはまだ実行中です。

ヒント

どの方法を使用する必要がありますか? コンストラクターは、不変性を強制し、テスト クラスの推論を容易にする readonly フィールドを使用できるため、一般的に推奨されます。 非同期初期化またはタイムアウトのサポートが必要な場合は、 TestInitialize を使用します。

また、両方の方法を組み合わせることもできます。 readonly フィールドの単純な同期初期化にはコンストラクターを使用し、それらのフィールドに依存する追加の非同期セットアップには TestInitialize を使用します。

必要に応じて、コード アナライザーを有効にして、一貫したアプローチを適用できます。

  • MSTEST0019 - コンストラクターよりも TestInitialize メソッドを優先する
  • MSTEST0020 - TestInitialize メソッドよりもコンストラクターを優先する

実行フェーズ

テスト メソッドは、セットアップが完了した後に実行されます。 asyncテスト メソッドの場合、MSTest は返されたTaskまたはValueTaskを待機します。

Warnung

非同期テスト メソッドには、既定では SynchronizationContext はありません。 これは、UI スレッドで実行される UWP および WinUI の UITestMethod テストには適用されません。

クリーンアップ フェーズ

テストごとのクリーンアップには、 TestCleanup または IDisposable/IAsyncDisposable を使用します。

[TestClass]
public class TestLevelCleanupExample
{
    private HttpClient? _client;

    [TestInitialize]
    public void TestInit()
    {
        _client = new HttpClient();
    }

    [TestCleanup]
    public void TestCleanup()
    {
        if (_client != null)
        {
            _client.Dispose();
        }
    }

    [TestMethod]
    public async Task GetData_ReturnsSuccess()
    {
        HttpResponseMessage response = await _client!.GetAsync("https://example.com");
        Assert.IsTrue(response.IsSuccessStatusCode);
    }
}

クリーンアップの実行順序 (基本に派生):

  1. TestCleanup (派生クラス)
  2. TestCleanup (基底クラス)
  3. DisposeAsync (実装されている場合)
  4. Dispose (実装されている場合)

ヒント

必要に応じて、コード アナライザーを有効にして、一貫したクリーンアップ アプローチを適用できます。

  • MSTEST0021 - TestCleanup メソッドよりも Dispose を優先する
  • MSTEST0022 - Dispose メソッドよりも TestCleanup を優先する

.NET コード分析ルールなど、MSTest 以外のアナライザーが有効になっている場合は、テスト クラスが破棄可能なリソースを所有している場合に破棄パターンを実装することを提案する CA1001 が表示されることがあります。 これは予期される動作であり、アナライザーのガイダンスに従う必要があります。

テスト段階の順序を完了する

  1. テスト クラス (コンストラクター) のインスタンスを作成する
  2. プロパティ TestContext 設定する (存在する場合)
  3. GlobalTestInitialize メソッドを実行する
  4. TestInitialize メソッドを基底から派生クラスまで実行する
  5. テスト メソッドの実行
  6. 結果で TestContext を更新する ( Outcome プロパティなど)
  7. TestCleanup メソッドを実行する (派生クラスから基本クラスへ)
  8. GlobalTestCleanup メソッドを実行する
  9. DisposeAsyncを実行する (実装されている場合)
  10. Disposeを実行する (実装されている場合)

ヒント

関連するアナライザー:

  • MSTEST0008 - 署名 TestInitialize 検証します。
  • MSTEST0009 - 署名 TestCleanup 検証します。
  • MSTEST0063 - テスト クラスコンストラクターを検証します。

ベスト プラクティス

  1. 適切なスコープを使用する: 冗長な作業を回避するために、セットアップを最も高いレベルに配置します。

  2. セットアップを高速に保つ: 実行時間の長いセットアップは、すべてのテストに影響します。 コストの高いリソースについては、遅延初期化を検討してください。

  3. 適切にクリーンアップする: 常にリソースをクリーンアップして、テストの干渉とメモリ リークを防ぎます。

  4. 非同期を正しく処理する: 戻り値の型async Taskを使用し、async voidではなく非同期ライフサイクル メソッドにしてください。

  5. テストの分離を検討する: 各テストは独立している必要があります。 テスト間で変更可能な状態を共有しないようにします。

  6. GlobalTest を慎重に使用する: グローバル ライフサイクル メソッドは、すべてのテストに対して実行されるため、軽量に保ちます。

こちらも参照ください