目次
概要
xUnit.netの [MemberData] 属性を使用し、テストメソッドに渡すパラメータを静的メソッド(またはプロパティ)から動的に供給する実装です。 [InlineData] では扱いにくい「配列」「オブジェクト」「動的に生成されるデータ」や「多数のテストケース」を管理するのに適しています。
仕様(入出力)
- データ供給元:
IEnumerable<object[]>を返すpublic staticメソッドを作成します。 - テストメソッド:
[MemberData]属性で供給元メソッドの名前を指定し、引数でデータを受け取ります。 - 動作: 供給されたデータの件数分、テストメソッドが繰り返し実行されます。
基本の使い方
// データ供給メソッド
public static IEnumerable<object[]> GetNames()
{
// { 期待値, 入力1, 入力2 }
yield return new object[] { "John Doe", "John", "Doe" };
yield return new object[] { "Jane Smith", "Jane", "Smith" };
}
[Theory]
[MemberData(nameof(GetNames))]
public void NameTest(string expected, string first, string last)
{
// ...
}
コード全文
氏名(First Name, Last Name)を結合してフルネームを生成するメソッドに対し、複数の名前パターンを検証するコードです。
using System;
using System.Collections.Generic;
using Xunit;
namespace ParameterizedTests
{
public class MemberDataSample
{
/// <summary>
/// テストデータを提供する静的メソッド
/// 戻り値は IEnumerable<object[]> である必要があります。
/// </summary>
public static IEnumerable<object[]> NameData()
{
// object配列の中身が、テストメソッドの引数(expected, first, last)にマッピングされます
yield return new object[] { "Taro Yamada", "Taro", "Yamada" };
yield return new object[] { "John Doe", "John", "Doe" };
yield return new object[] { "Tech User", "Tech", "User" };
}
/// <summary>
/// MemberDataで指定したメソッドからデータを取得して実行
/// </summary>
[Theory]
[MemberData(nameof(NameData))]
public void GetFullName_ReturnsCombinedString(string expected, string first, string last)
{
// Arrange
var generator = new NameGenerator();
// Act
var result = generator.GetFullName(first, last);
// Assert
Assert.Equal(expected, result);
}
}
/// <summary>
/// テスト対象のクラス
/// </summary>
public class NameGenerator
{
public string GetFullName(string firstName, string lastName)
{
return $"{firstName} {lastName}";
}
}
}
カスタムポイント
- プロパティの使用: メソッドだけでなく、静的プロパティもデータソースとして指定できます。C#
public static TheoryData<string, string, string> TestCases => new TheoryData<string, string, string> { { "A B", "A", "B" }, { "X Y", "X", "Y" } };※TheoryDataクラスを使うと型安全にデータを定義できます。 - 外部クラスの利用: テストデータ定義を別クラスに分離したい場合は、
MemberTypeプロパティを使用します。[MemberData(nameof(ExternalData.GetData), MemberType = typeof(ExternalData))]
注意点
- 静的メンバであること: データを提供するメソッドやプロパティは必ず
staticである必要があります。 - 型と順序の一致:
new object[] { ... }の中に記述するデータの「型」と「並び順」は、テストメソッドの引数定義と完全に一致させてください。不一致の場合、実行時に型変換エラーが発生します。 - 可視性: データ提供メンバは基本的に
publicである必要があります。
応用
引数付きMemberDataによる動的生成
データ生成メソッド自体にパラメータを渡し、取得するテストデータの件数や範囲を制御する例です。
public static IEnumerable<object[]> GetRange(int count)
{
for (int i = 1; i <= count; i++)
{
yield return new object[] { i };
}
}
// 5件のデータを生成してテスト
[Theory]
[MemberData(nameof(GetRange), parameters: 5)]
public void CheckPositiveNumbers(int number)
{
Assert.True(number > 0);
}
まとめ
[MemberData] を利用することで、テストロジック([Theory]メソッド)とテストデータ(staticメソッド)を分離できます。これにより、データの追加・変更が容易になり、特に複雑なオブジェクトや大量のパターンを検証する際にテストコードの可読性を高く保つことができます。
