次の方法で共有


MSTest でのテストの実行と制御

MSTest には、並列化、スレッド モデル、タイムアウト、再試行、プラットフォームまたは環境に基づく条件付き実行など、テストの実行方法を制御するための属性が用意されています。

スレッド属性

スレッド属性は、使用するスレッド モデルのテスト メソッドを制御します。 これらの属性は、特定のスレッド要件を持つ COM コンポーネント、UI 要素、またはコードをテストする場合に不可欠です。

STATestClassAttribute

STATestClassAttributeは、シングル スレッド アパートメント (STA) 内のクラス (ClassInitializeClassCleanupを含む) 内のすべてのテスト メソッドを実行します。 STA を必要とする COM オブジェクトをテストする場合は、この属性を使用します。

[STATestClass]
public class ComInteropTests
{
    [TestMethod]
    public void TestComComponent()
    {
        // This test runs in an STA thread
        var comObject = new SomeComObject();
        // Test COM interactions
    }
}

この属性は、MSTest v3.6 以降の Windows でのみサポートされています。

STATestMethodAttribute

STATestMethodAttributeは、シングルスレッド アパートメントで特定のテスト メソッドを実行します。 この属性は、STA を必要とする個々のテストには使用しますが、クラス内の他のテストでは使用しません。

[TestClass]
public class MixedThreadingTests
{
    [STATestMethod]
    public void TestRequiringSTA()
    {
        // This test runs in an STA thread
    }

    [TestMethod]
    public void RegularTest()
    {
        // This test uses default threading
    }
}

この属性は、MSTest v3.6 以降の Windows でのみサポートされています。

非同期継続のために STA コンテキストを保持する

MSTest 4.1 以降、 STATestMethodAttribute には、同じ STA スレッドで非同期継続が確実に実行される UseSTASynchronizationContext プロパティが含まれています。 この属性を有効にすると、継続を STA スレッドにポストバックするカスタム SynchronizationContext が作成されます。これは、非同期操作全体で STA スレッド処理を必要とする UI コンポーネントをテストするために不可欠です。

[TestClass]
public class UIComponentTests
{
    [STATestMethod(UseSTASynchronizationContext = true)]
    public async Task TestAsyncUIOperation()
    {
        // Initial code runs on STA thread
        var control = new MyControl();
        
        await control.LoadDataAsync();
        
        // Continuation also runs on STA thread,
        // ensuring UI operations remain valid
        Assert.IsTrue(control.IsDataLoaded);
    }
}

ヒント

非同期操作を実行し、その継続が同じスレッドで実行されることを期待する Windows フォームまたは WPF コンポーネントをテストする場合は、 UseSTASynchronizationContext = true を使用します。

UITestMethodAttribute

UITestMethod属性は、UI スレッドでのテスト実行をスケジュールします。 この属性は、UI スレッド アクセスを必要とする UWP アプリケーションと WinUI アプリケーションをテストするために設計されています。

[TestClass]
public class WinUITests
{
    [UITestMethod]
    public void TestUIComponent()
    {
        // This test runs on the UI thread
        var button = new Button();
        button.Content = "Click me";
        Assert.IsNotNull(button.Content);
    }
}

この属性には、UWP または WinUI プラットフォームに適した MSTest アダプターが必要です。 詳細については、 プラットフォームのサポート に関するセクションを参照してください。

並列化属性

並列化属性は、テストを同時に実行するかどうかを制御し、テストの実行時間を短縮します。

ParallelizeAttribute

既定では、MSTest はテストを順番に実行します。 ParallelizeAttribute アセンブリ レベルの属性を使用すると、並列テストを実行できます。

using Microsoft.VisualStudio.TestTools.UnitTesting;

[assembly: Parallelize(Workers = 0, Scope = ExecutionScope.MethodLevel)]

並列化スコープ

Scope 行動
ClassLevel 複数のテスト クラスが並列で実行されますが、クラス内のテストは順番に実行されます
MethodLevel 個々のテスト メソッドは、クラスに関係なく並列で実行できます

ワーカー スレッド

Workers プロパティは、並列実行のスレッドの最大数を指定します。

  • 0 (既定値): コンピューター上の論理プロセッサの数を使用します
  • 任意の正の整数: その特定の数のスレッドを使用します
// Parallelize at class level with 2 worker threads
[assembly: Parallelize(Workers = 2, Scope = ExecutionScope.ClassLevel)]

ヒント

また、コードを変更せずに 、runsettings または testconfig.json を使用して並列化を構成することもできます。

ヒント

多くのテストで現在順次実行が必要な場合でも、既定ではアセンブリ レベルで並列化を有効にします。 この方法では、最初から並列実行をサポートする新しいテストを作成することをお勧めします。 MSTEST0001 アナライザーを使用して、アセンブリが[assembly: Parallelize]または[assembly: DoNotParallelize]を使用して並列化の意図を明示的に宣言していることを確認します。 並列化が有効になったら、各テスト クラスを確認して、同時実行が安全にサポートされているかどうかを判断します。 多くの場合、 DoNotParallelize を含む少数のクラスまたはメソッドのみを除外するだけで十分です。これにより、テストの大部分を並列で実行して、テストの実行を大幅に高速化できます。

DoNotParallelizeAttribute

この DoNotParallelizeAttribute では、特定のアセンブリ、クラス、またはメソッドに対する並列実行が禁止されます。 この属性は、同時に安全にアクセスできない状態またはリソースをテストで共有する場合に使用します。

[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]

[TestClass]
public class ParallelTests
{
    [TestMethod]
    public void CanRunInParallel()
    {
        // This test can run with others
    }
}

[TestClass]
[DoNotParallelize]
public class SequentialTests
{
    [TestMethod]
    public void MustRunSequentially()
    {
        // This class's tests run sequentially
    }
}

[TestClass]
public class MixedTests
{
    [TestMethod]
    public void CanRunInParallel()
    {
        // This test can run with others
    }

    [TestMethod]
    [DoNotParallelize]
    public void MustBeIsolated()
    {
        // This specific test doesn't run in parallel
    }
}

DoNotParallelize属性を使用して並列実行を有効にした場合にのみ、Parallelizeが必要です。

タイムアウト属性

タイムアウト属性は、テストが無期限に実行されるのを防ぎ、パフォーマンスの問題を特定するのに役立ちます。

TimeoutAttribute

TimeoutAttributeは、テストメソッドまたはフィクスチャ メソッドが実行できる最大時間 (ミリ秒) を指定します。 実行がこの時間を超えると、テストは失敗します。

[TestClass]
public class TimeoutTests
{
    [TestMethod]
    [Timeout(5000)] // 5 seconds
    public void TestWithTimeout()
    {
        // Test must complete within 5 seconds
    }
}

ヒント

コードを変更することなく、 runsettings (TestTimeout) または testconfig.json (timeout.test) を使用してグローバル テスト タイムアウトを構成できます。

フィクスチャ メソッドにタイムアウトを適用する

初期化メソッドとクリーンアップ メソッドにタイムアウトを適用することもできます。

[TestClass]
public class FixtureTimeoutTests
{
    [ClassInitialize]
    [Timeout(10000)]
    public static void ClassInit(TestContext context)
    {
        // Must complete within 10 seconds
    }

    [TestInitialize]
    [Timeout(2000)]
    public void TestInit()
    {
        // Must complete within 2 seconds
    }
}

ヒント

[Timeout]属性を受け入れるすべてのフィクスチャ メソッドには、同等のグローバル構成設定があります。 runsettings または testconfig.json を使用して、TestInitializeTimeout、およびそれらのクリーンアップの設定を通じてタイムアウトをグローバルに構成します。

タイムアウトは正確であるとは限りません。 指定した時間が経過するとテストは中止されますが、実際の取り消しには少し時間がかかる場合があります。

協力によるキャンセル

既定では、MSTest は各タイミング テスト メソッドを個別のタスクまたはスレッドでラップします。 タイムアウトに達すると、フレームワークはテストの監視を停止しますが、基になるタスクはバックグラウンドで実行され続けます。 この動作により、次の問題が発生する可能性があります。

  • テスト メソッドは、タイムアウト後もリソースにアクセスし、状態を変更し続けます。
  • バックグラウンド実行により、レースコンディションが後続のテストに影響を与える可能性があります。
  • 各時間計測メソッドでは、タスク/スレッド ラッパーにより追加のオーバーヘッドが発生します。

MSTest 3.6 以降では、 CooperativeCancellation プロパティを使用してこれらの問題を回避します。 協調モードでは、MSTest は追加のタスクでテストをラップしません。 代わりに、タイムアウトに達すると、フレームワークはキャンセル トークンを通知します。 テスト コードは、トークンを定期的にチェックし、正常に終了する役割を担います。

[TestClass]
public class CooperativeTimeoutTests
{
    [TestMethod]
    [Timeout(5000, CooperativeCancellation = true)]
    public async Task TestWithCooperativeCancellation(CancellationToken cancellationToken)
    {
        // Check the token periodically
        while (!cancellationToken.IsCancellationRequested)
        {
            await Task.Delay(100, cancellationToken);
            // Do work
        }
    }

    [TestMethod]
    [Timeout(5000, CooperativeCancellation = true)]
    public void SyncTestWithCooperativeCancellation(CancellationToken cancellationToken)
    {
        // Works with sync methods too
        for (int i = 0; i < 1000; i++)
        {
            cancellationToken.ThrowIfCancellationRequested();
            // Do work
        }
    }
}

協調キャンセルの利点:

  • パフォーマンスのオーバーヘッドが低くなります (テストごとに追加のタスク/スレッド ラッパーはありません)。
  • コードがキャンセルを明示的に処理することで、リソースのクリーンアップがより効率的になります。
  • 標準の .NET キャンセル パターンに合わせて調整します。
  • テスト コードと監視されていないバックグラウンド実行の間の競合状態を回避することで、決定論的な動作を行います。

協調的な取り消しでは、テスト コードでキャンセル トークンを定期的に確認する必要があります。 コードでトークンがチェックされない場合、タイムアウトに達してもテストは実際には停止しません。

ヒント

各属性に個別に設定する代わりに、 runsettings または testconfig.json を使用して、すべてのタイムアウト属性に対して協調キャンセルをグローバルに有効にすることができます。

ヒント

関連するアナライザー:

  • MSTEST0045 - タイムアウト属性に協調キャンセルを使用することをお勧めします。

再試行属性

再試行属性は、失敗したテストを自動的に再実行することで、不安定なテストを処理するのに役立ちます。

RetryAttribute

MSTest 3.8 で導入された RetryAttributeは、失敗またはタイムアウトしたテスト メソッドを自動的に再試行します。最大再試行回数、再試行間隔、バックオフ戦略を構成します。

[TestClass]
public class RetryTests
{
    [TestMethod]
    [Retry(3)] // Retry up to 3 times if the test fails
    public void FlakeyNetworkTest()
    {
        // Test that might occasionally fail due to network issues
    }

    [TestMethod]
    [Retry(3, MillisecondsDelayBetweenRetries = 1000, BackoffType = DelayBackoffType.Exponential)]
    public void TestWithExponentialBackoff()
    {
        // Retries with increasing delays: 1s, 2s, 4s
    }

    [TestMethod]
    [Retry(5, MillisecondsDelayBetweenRetries = 500, BackoffType = DelayBackoffType.Constant)]
    public void TestWithConstantDelay()
    {
        // Retries with constant 500ms delay between attempts
    }
}

構成オプション

プロパティ Description 既定値
MaxRetryAttempts 再試行の最大数 (読み取り専用、コンストラクター経由で設定) 必須
MillisecondsDelayBetweenRetries 再試行間の基本遅延 (ミリ秒) 0
BackoffType Constant または Exponential 遅延 Constant

テスト メソッドに存在できる RetryAttribute は 1 つだけです。 RetryAttributeでマークされていないメソッドでは、TestMethodを使用できません。

ヒント

関連するアナライザー:

  • MSTEST0043 - テスト メソッドで RetryAttribute を使用することをお勧めします。

カスタム再試行の実装

RetryBaseAttributeから継承してカスタム再試行ロジックを作成します。

public class CustomRetryAttribute : RetryBaseAttribute
{
    private readonly int _maxRetries;

    public CustomRetryAttribute(int maxRetries)
    {
        _maxRetries = maxRetries;
    }

    // Implement abstract members
    // Add custom logic for retry conditions
}

条件付き実行属性

条件付き実行属性は、オペレーティング システムや CI 環境などの特定の条件に基づいてテストを実行するかどうかを制御します。

ConditionBaseAttribute

ConditionBaseAttributeは、条件付き実行の抽象基本クラスです。 MSTest には、いくつかの組み込み実装が用意されています。

既定では、条件属性は継承されません。 基底クラスに適用しても、派生クラスには影響しません。 カスタム条件属性は、 AttributeUsageを再定義することでこの動作をオーバーライドできますが、組み込みの条件属性との整合性を維持することはお勧めしません。

ヒント

関連するアナライザー:

  • MSTEST0041 - テスト クラスで条件ベースの属性を使用することをお勧めします。

OSConditionAttribute

OSConditionAttributeは、オペレーティング システムに基づいてテストを実行またはスキップします。 OperatingSystems フラグ列挙型を使用して、適用するオペレーティング システムを指定します。

[TestClass]
public class OSSpecificTests
{
    [TestMethod]
    [OSCondition(OperatingSystems.Windows)]
    public void WindowsOnlyTest()
    {
        // Runs only on Windows
    }

    [TestMethod]
    [OSCondition(OperatingSystems.Linux | OperatingSystems.OSX)]
    public void UnixLikeOnlyTest()
    {
        // Runs on Linux or macOS
    }

    [TestMethod]
    [OSCondition(ConditionMode.Exclude, OperatingSystems.Windows)]
    public void SkipOnWindowsTest()
    {
        // Runs on any OS except Windows
    }
}

サポートされるオペレーティング システム

OS Description
Windows Microsoft Windows
Linux Linux ディストリビューション
OSX macOS
FreeBSD FreeBSD

オペレーティング システムをビットごとの OR 演算子 (|) と組み合わせます。

ヒント

関連するアナライザー:

  • MSTEST0061 - ランタイム チェックの代わりに OSCondition 属性を使用することをお勧めします。

CIConditionAttribute

CIConditionAttributeは、継続的インテグレーション環境で実行されているかどうかに基づいてテストを実行またはスキップします。

[TestClass]
public class CIAwareTests
{
    [TestMethod]
    [CICondition] // Default: runs only in CI
    public void CIOnlyTest()
    {
        // Runs only in CI environments
    }

    [TestMethod]
    [CICondition(ConditionMode.Include)]
    public void ExplicitCIOnlyTest()
    {
        // Same as above, explicitly stated
    }

    [TestMethod]
    [CICondition(ConditionMode.Exclude)]
    public void LocalDevelopmentOnlyTest()
    {
        // Skipped in CI, runs during local development
    }
}

IgnoreAttribute

IgnoreAttributeは無条件にテスト クラスまたはメソッドをスキップします。 必要に応じて、無視する理由を指定します。

ヒント

関連アナライザー: MSTEST0015 - テスト メソッドは無視しないでください。 このアナライザーを有効にして、永続的に無視されるテストを検出します。

[TestClass]
public class IgnoreExamples
{
    [TestMethod]
    [Ignore]
    public void TemporarilyDisabled()
    {
        // This test is skipped
    }

    [TestMethod]
    [Ignore("Waiting for bug #123 to be fixed")]
    public void DisabledWithReason()
    {
        // This test is skipped with a documented reason
    }
}

[TestClass]
[Ignore("Entire class needs refactoring")]
public class IgnoredTestClass
{
    [TestMethod]
    public void Test1() { }  // Skipped

    [TestMethod]
    public void Test2() { }  // Skipped
}

既知の問題が原因でテストを無視する場合は、追跡可能性のために WorkItemAttribute または GitHubWorkItemAttribute を使用します。

[TestClass]
public class TrackedIgnoreExamples
{
    [TestMethod]
    [Ignore("Waiting for fix")]
    [WorkItem(12345)]
    public void TestWithWorkItem()
    {
        // Linked to work item 12345
    }

    [TestMethod]
    [Ignore("Known issue")]
    [GitHubWorkItem("https://github.com/owner/repo/issues/42")]
    public void TestWithGitHubIssue()
    {
        // Linked to GitHub issue #42
    }
}

ベスト プラクティス

  1. 並列化を賢明に使用する: 独立したテストでは並列化を有効にしますが、状態を共有するテストには DoNotParallelize を使用します。

  2. 適切なタイムアウトを設定する: 通常の実行を許可するが、スタックしたテストをキャッチするタイムアウトを選択します。 遅い CI 環境を検討してください。

  3. 協力的な取り消しを優先する: 協調的な取り消しを使用して、余分なタスク ラッパーのオーバーヘッドを回避し、タイムアウトしたテストのバックグラウンド実行を防ぎます。 このプラクティスを適用するには 、MSTEST0045 アナライザーを有効にします。

  4. 無視されたテストのドキュメント: テストを無視する場合は、常に理由と作業項目の参照を指定します。

  5. 再試行を控えめに使用する: 再試行に依存するのではなく、不安定なテストの根本原因に対処します。

  6. OS 固有のコードを適切にテストする: OSCondition を使用して、該当する場所でのみプラットフォーム固有のテストを実行します。

こちらも参照ください