データ ドリブン テストでは、複数の入力データ セットで同じテスト メソッドを実行できます。 テスト ケースごとに個別のテスト メソッドを記述する代わりに、テスト ロジックを 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を返し、 MethodInfo と object[]の 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
}
}
ベスト プラクティス
適切な属性を選択します。単純なインライン データには
DataRowを使用します。 複雑なデータまたは計算されたデータにはDynamicDataを使用します。テスト ケースに名前を付ける:
DisplayNameを使用して、テストの失敗を識別しやすくします。データ ソースを閉じる: 可能な場合は同じクラスでデータ ソースを定義し、保守容易性を高めます。
意味のあるデータを使用する: エッジ ケースと境界条件を実行するテスト データを選択します。
組み合わせテストを検討する: パラメーターの組み合わせをテストする場合は、 Combinatorial.MSTest パッケージを使用します。
こちらも参照ください
.NET