次の方法で共有


MSTest でのデータ ドリブン テスト

データ ドリブン テストでは、複数の入力データ セットで同じテスト メソッドを実行できます。 テスト ケースごとに個別のテスト メソッドを記述する代わりに、テスト ロジックを 1 回定義し、属性または外部データ ソースを介して異なる入力を提供します。

概要

MSTest には、データ ドリブン テストに関するいくつかの属性が用意されています。

特性 利用シーン 最適な用途
DataRow インライン テスト データ 単純で静的なテスト ケース
DynamicData メソッド、プロパティ、またはフィールドからのデータ 複雑なテストデータおよび計算済みのテストデータ
DataSource 外部データ ファイルまたはデータベース 外部データ ソースを使用した従来のシナリオ

MSTest には、データ ドリブン シナリオを拡張するための次の種類も用意されています。

  • TestDataRow<T>: 表示名、カテゴリ、個々のテスト ケースへのメッセージの無視などのメタデータ サポートを追加する ITestDataSource 実装 ( DynamicDataを含む) の戻り値の型。
  • ITestDataSource: カスタム属性に実装して、完全なカスタム データ ソース属性を作成できるインターフェイス。

ヒント

組み合わせテスト (複数のパラメーター セットのすべての組み合わせをテストする) には、オープンソースの Combinatorial.MSTest NuGet パッケージを使用します。 このコミュニティで管理されるパッケージは GitHub で入手できますが 、Microsoft では管理されていません。

DataRowAttribute

DataRowAttributeを使用すると、複数の異なる入力で同じテスト メソッドを実行できます。 1 つまたは複数の DataRow 属性をテスト メソッドに適用し、 TestMethodAttributeと組み合わせます。

引数の数と型は、テスト メソッドのシグネチャと完全に一致している必要があります。

ヒント

関連するアナライザー:

  • MSTEST0014 は、 DataRow 引数がテスト メソッドシグネチャと一致することを検証します。
  • MSTEST0042 は、同じテスト ケースを複数回実行する重複する DataRow エントリを検出します。

基本的な使用方法

[TestClass]
public class CalculatorTests
{
    [TestMethod]
    [DataRow(1, 2, 3)]
    [DataRow(0, 0, 0)]
    [DataRow(-1, 1, 0)]
    [DataRow(100, 200, 300)]
    public void Add_ReturnsCorrectSum(int a, int b, int expected)
    {
        var calculator = new Calculator();
        Assert.AreEqual(expected, calculator.Add(a, b));
    }
}

サポートされている引数の型

DataRow では、プリミティブ、文字列、配列、null 値など、さまざまな引数型がサポートされています。

[TestClass]
public class DataRowExamples
{
    [TestMethod]
    [DataRow(1, "message", true, 2.0)]
    public void TestWithMixedTypes(int i, string s, bool b, float f)
    {
        // Test with different primitive types
    }

    [TestMethod]
    [DataRow(new string[] { "line1", "line2" })]
    public void TestWithArray(string[] lines)
    {
        Assert.AreEqual(2, lines.Length);
    }

    [TestMethod]
    [DataRow(null)]
    public void TestWithNull(object o)
    {
        Assert.IsNull(o);
    }

    [TestMethod]
    [DataRow(new string[] { "a", "b" }, new string[] { "c", "d" })]
    public void TestWithMultipleArrays(string[] input, string[] expected)
    {
        // Starting with MSTest v3, two arrays don't need wrapping
    }
}

変数引数にパラメーターを使用する

params キーワードを使用して、可変個の引数を受け入れます。

[TestClass]
public class ParamsExample
{
    [TestMethod]
    [DataRow(1, 2, 3, 4)]
    [DataRow(10, 20)]
    [DataRow(5)]
    public void TestWithParams(params int[] values)
    {
        Assert.IsTrue(values.Length > 0);
    }
}

カスタム表示名

DisplayName プロパティを設定して、テスト エクスプローラーでのテスト ケースの表示方法をカスタマイズします。

[TestClass]
public class DisplayNameExample
{
    [TestMethod]
    [DataRow(1, 2, DisplayName = "Functional Case FC100.1")]
    [DataRow(3, 4, DisplayName = "Edge case: small numbers")]
    public void TestMethod(int i, int j)
    {
        Assert.IsTrue(i < j);
    }
}

ヒント

テスト メタデータをより詳細に制御する場合は、< を使用することを検討してください。 TestDataRow<T> は、テスト カテゴリと共に表示名をサポートし、個々のテスト ケースのメッセージを無視します。

特定のテスト ケースを無視する

MSTest v3.8 以降では、 IgnoreMessage プロパティを使用して特定のデータ行をスキップします。

[TestClass]
public class IgnoreDataRowExample
{
    [TestMethod]
    [DataRow(1, 2)]
    [DataRow(3, 4, IgnoreMessage = "Temporarily disabled - bug #123")]
    [DataRow(5, 6)]
    public void TestMethod(int i, int j)
    {
        // Only the first and third data rows run
        // The second is skipped with the provided message
    }
}

DynamicDataAttribute

DynamicDataAttributeを使用すると、メソッド、プロパティ、またはフィールドからテスト データを提供できます。 テスト データが複雑な場合、動的に計算される場合、またはインライン DataRow 属性に対して詳細すぎる場合は、この属性を使用します。

サポートされているデータ ソースの種類

データ ソースは、IEnumerable<T>が次の表に示す型のいずれかである任意のTを返すことができます。 IEnumerable<T>List<T>などの配列、カスタム コレクション型など、T[]を実装するすべてのコレクションが機能します。 ニーズに基づいて選択します。

戻り値の型 型安全性 メタデータのサポート 最適な用途
ValueTuple (例: (int, string)) コンパイル時 いいえ ほとんどのシナリオ - 完全な型チェックを使用した単純な構文
Tuple<...> コンパイル時 いいえ ValueTuple を使用できない場合
TestDataRow<T> コンパイル時 イエス 表示名、カテゴリ、または無視メッセージを設定する必要があるテストケース
object[] ランタイムのみ いいえ 新しいテストでは既存コードを避ける

ヒント

新しいテスト データ メソッドの場合は、単純なケースに ValueTuple を使用するか、メタデータが必要な場合に TestDataRow<T> します。 コンパイル時の型チェックがなく、型の不一致によってランタイム エラーが発生する可能性があるため、 object[] は避けてください。

データ ソース

データ ソースには、メソッド、プロパティ、またはフィールドを指定できます。 3 つはすべて交換可能です。好みに応じて選択します。

[TestClass]
public class DynamicDataExample
{
    // Method - best for computed or yielded data
    public static IEnumerable<(int Value, string Name)> GetTestData()
    {
        yield return (1, "first");
        yield return (2, "second");
    }

    // Property - concise for static data
    public static IEnumerable<(int Value, string Name)> TestDataProperty =>
    [
        (1, "first"),
        (2, "second")
    ];

    // Field - simplest for static data
    public static IEnumerable<(int Value, string Name)> TestDataField =
    [
        (1, "first"),
        (2, "second")
    ];

    [TestMethod]
    [DynamicData(nameof(GetTestData))]
    public void TestWithMethod(int value, string name)
    {
        Assert.IsTrue(value > 0);
    }

    [TestMethod]
    [DynamicData(nameof(TestDataProperty))]
    public void TestWithProperty(int value, string name)
    {
        Assert.IsTrue(value > 0);
    }

    [TestMethod]
    [DynamicData(nameof(TestDataField))]
    public void TestWithField(int value, string name)
    {
        Assert.IsTrue(value > 0);
    }
}

データ ソースのメソッド、プロパティ、フィールドは public static し、サポートされている型の IEnumerable<T> を返す必要があります。

ヒント

関連アナライザー: MSTEST0018 は、データ ソースが存在し、アクセス可能であり、正しい署名を持っていることを検証します。

別のクラスのデータ ソース

型パラメーターを使用して別のクラスを指定します。

public class TestDataProvider
{
    public static IEnumerable<(int, string)> GetTestData()
    {
        yield return (1, "first");
        yield return (2, "second");
    }
}

[TestClass]
public class DynamicDataExternalExample
{
    [TestMethod]
    [DynamicData(nameof(TestDataProvider.GetTestData), typeof(TestDataProvider))]
    public void TestMethod(int value1, string value2)
    {
        Assert.IsTrue(value1 > 0);
    }
}

カスタム表示名

DynamicDataDisplayName プロパティを使用してテスト ケースの表示名をカスタマイズします。

using System.Reflection;

[TestClass]
public class DynamicDataDisplayNameExample
{
    [TestMethod]
    [DynamicData(nameof(GetTestData), DynamicDataDisplayName = nameof(GetDisplayName))]
    public void TestMethod(int value1, string value2)
    {
        Assert.IsTrue(value1 > 0);
    }

    public static IEnumerable<(int, string)> GetTestData()
    {
        yield return (1, "first");
        yield return (2, "second");
    }

    public static string GetDisplayName(MethodInfo methodInfo, object[] data)
    {
        return $"{methodInfo.Name} with value {data[0]} and '{data[1]}'";
    }
}

表示名メソッドは public staticし、 stringを返し、 MethodInfoobject[]の 2 つのパラメーターを受け入れる必要があります。

ヒント

カスタム表示名に対するより簡単な方法については、別のメソッドではなく、< プロパティで > を使用することを検討してください。

データ ソースからのすべてのテスト ケースを無視する

MSTest v3.8 以降では、 IgnoreMessage を使用してすべてのテスト ケースをスキップします。

[TestClass]
public class IgnoreDynamicDataExample
{
    [TestMethod]
    [DynamicData(nameof(GetTestData), IgnoreMessage = "Feature not ready")]
    public void TestMethod(int value1, string value2)
    {
        // All test cases from GetTestData are skipped
    }

    public static IEnumerable<(int, string)> GetTestData()
    {
        yield return (1, "first");
        yield return (2, "second");
    }
}

ヒント

個々のテスト ケースを無視するには、TestDataRow<T> プロパティでIgnoreMessageを使用します。 TestDataRow<T> セクションを参照してください。

TestDataRow

TestDataRow<T> クラスは、データ ドリブン テストのテスト データの制御を強化します。 データ ソースの戻り値の型として IEnumerable<TestDataRow<T>> を使用して、次の値を指定します。

  • カスタム表示名: テスト ケースごとに一意の表示名を設定する
  • テスト カテゴリ: 個々のテスト ケースにメタデータをアタッチする
  • メッセージを無視する: 理由がある特定のテスト ケースをスキップする
  • 型セーフ なデータ: 厳密に型指定されたテスト データにジェネリックを使用する

基本的な使用方法

[TestClass]
public class TestDataRowExample
{
    [TestMethod]
    [DynamicData(nameof(GetTestDataRows))]
    public void TestMethod(int value1, string value2)
    {
        Assert.IsTrue(value1 > 0);
    }

    public static IEnumerable<TestDataRow<(int, string)>> GetTestDataRows()
    {
        yield return new TestDataRow<(int, string)>((1, "first"))
        {
            DisplayName = "Test Case 1: Basic scenario",
        };

        yield return new TestDataRow<(int, string)>((2, "second"))
        {
            DisplayName = "Test Case 2: Edge case",
            TestCategories = ["HighPriority", "Critical"],
        };

        yield return new TestDataRow<(int, string)>((3, "third"))
        {
            IgnoreMessage = "Not yet implemented",
        };
    }
}

DataSourceAttribute

DataSource は .NET Framework でのみ使用できます。 .NET (Core) プロジェクトの場合は、代わりに DataRow または DynamicData を使用します。

DataSourceAttributeは、CSV ファイル、XML ファイル、データベースなどの外部データ ソースにテストを接続します。

詳細については、以下を参照してください。

ITestDataSource

ITestDataSource インターフェイスを使用すると、完全にカスタムのデータ ソース属性を作成できます。 環境変数、構成ファイル、またはその他のランタイム条件に基づくテスト データの生成など、組み込みの属性でサポートされていない動作が必要な場合は、このインターフェイスを実装します。

インターフェイス メンバー

インターフェイスでは、次の 2 つのメソッドを定義します。

メソッド 目的
GetData(MethodInfo) テスト データを次のように返します。 IEnumerable<object?[]>
GetDisplayName(MethodInfo, object?[]?) テスト ケースの表示名を返します。

カスタム データ ソース属性の作成

カスタム データ ソースを作成するには、 Attribute から継承し、 ITestDataSourceを実装する属性クラスを定義します。

using System.Globalization;
using System.Reflection;

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class MyDataSourceAttribute : Attribute, ITestDataSource
{
    public IEnumerable<object?[]> GetData(MethodInfo methodInfo)
    {
        // Return test data based on your custom logic
        yield return [1, "first"];
        yield return [2, "second"];
        yield return [3, "third"];
    }

    public string? GetDisplayName(MethodInfo methodInfo, object?[]? data)
    {
        return data is null
            ? null
            : string.Format(CultureInfo.CurrentCulture, "{0} ({1})", methodInfo.Name, string.Join(",", data));
    }
}

[TestClass]
public class CustomDataSourceExample
{
    [TestMethod]
    [MyDataSource]
    public void TestWithCustomDataSource(int value, string name)
    {
        Assert.IsTrue(value > 0);
        Assert.IsNotNull(name);
    }
}

実際の例: 環境ベースのテスト データ

この例では、ターゲット フレームワークに基づいてテスト データを生成し、オペレーティング システムに基づいてフィルター処理するカスタム属性を示します。

using System.Globalization;
using System.Reflection;

[AttributeUsage(AttributeTargets.Method)]
public class TargetFrameworkDataAttribute : Attribute, ITestDataSource
{
    private readonly string[] _frameworks;

    public TargetFrameworkDataAttribute(params string[] frameworks)
    {
        _frameworks = frameworks;
    }

    public IEnumerable<object?[]> GetData(MethodInfo methodInfo)
    {
        bool isWindows = OperatingSystem.IsWindows();

        foreach (string framework in _frameworks)
        {
            // Skip .NET Framework on non-Windows platforms
            if (!isWindows && framework.StartsWith("net4", StringComparison.Ordinal))
            {
                continue;
            }

            yield return [framework];
        }
    }

    public string? GetDisplayName(MethodInfo methodInfo, object?[]? data)
    {
        return data is null
            ? null
            : string.Format(CultureInfo.CurrentCulture, "{0} ({1})", methodInfo.Name, data[0]);
    }
}

[TestClass]
public class CrossPlatformTests
{
    [TestMethod]
    [TargetFrameworkData("net48", "net8.0", "net9.0")]
    public void TestOnMultipleFrameworks(string targetFramework)
    {
        // Test runs once per applicable framework
        Assert.IsNotNull(targetFramework);
    }
}

ヒント

表示名をカスタマイズしたりメタデータを追加したりするだけの単純なケースでは、<を実装する代わりに> を使用することを検討してください。 動的フィルター処理または複雑なデータ生成ロジックを必要とするシナリオに対して、カスタム ITestDataSource 実装を予約します。

展開戦略

データ ドリブン テスト属性は、テスト エクスプローラーと TRX 結果でのテスト ケースの表示方法を制御する TestDataSourceUnfoldingStrategy プロパティをサポートします。 このプロパティは、個々のテスト ケースを個別に実行できるかどうかを決定します。

利用可能な戦略

戦略 行動
Auto (既定値) MSTest が展開する最適な戦略を決定する
Unfold すべてのテスト ケースが個別に展開および表示されます
Fold すべてのテストケースが単一のテストノードに統合されます

戦略を変更するタイミング

ほとんどのシナリオでは、既定の Auto 動作が最適なバランスを提供します。 特定の要件がある場合にのみ、この設定を変更することを検討してください。

  • 非決定論的なデータ ソース
  • MSTest の既知の制限事項またはバグ
  • 多くのテストケースに関する性能問題

使用例

[TestClass]
public class UnfoldingExample
{
    [TestMethod(UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Unfold)] // That's the default behavior
    [DataRow(1, "one")]
    [DataRow(2, "two")]
    [DataRow(3, "three")]
    public void TestMethodWithUnfolding(int value, string text)
    {
        // Each test case appears individually in Test Explorer
    }

    [TestMethod(UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Fold)]
    [DataRow(1, "one")]
    [DataRow(2, "two")]
    [DataRow(3, "three")]
    public void TestMethodWithFolding(int value, string text)
    {
        // All test cases appear as a single collapsed node
    }
}

ベスト プラクティス

  1. 適切な属性を選択します。単純なインライン データには DataRow を使用します。 複雑なデータまたは計算されたデータには DynamicData を使用します。

  2. テスト ケースに名前を付ける: DisplayName を使用して、テストの失敗を識別しやすくします。

  3. データ ソースを閉じる: 可能な場合は同じクラスでデータ ソースを定義し、保守容易性を高めます。

  4. 意味のあるデータを使用する: エッジ ケースと境界条件を実行するテスト データを選択します。

  5. 組み合わせテストを検討する: パラメーターの組み合わせをテストする場合は、 Combinatorial.MSTest パッケージを使用します。

こちらも参照ください