{
    "title": "ソースの指定 - xUnit.net",
    "useMathjax": false,
    "isDraft": true
}

================================================================================

  • 単体テストでは、引数と結果だけが違って、処理の同じテストを行いたいケースが多い。
  • テストを関数として準備してやり、そこに引数を順次入れてテストを実行するための便利な機能が xUnit.net には準備されている。

InlineData 属性

  • テスト用の関数に属性としてつける。
  • 属性の引数に書いたものがテストとして実行する際の引数になる。
  • 1つの関数に何回でもつけることができる。
  • 対象となる関数には Theory 属性をつけないとテストは実行されない。(逆に [Fact] は必要ない)
  • InlineData の引数はすべて object 型になるため実行するまで型があってるかの情報は得られない。
[Theory,
InlineData(0, 0, 0),
InlineData(1, 0, 1),
InlineData(1, 1, 0),
InlineData(2, 1, 1)]
public void AddTest(int expected, int x, int y)
{
    Assert.Equal(expected, Add(x, y));
}

MemberData 属性

  • MemberDataAttribute
  • テスト用の関数に属性としてつける
  • 第1引数に指定した名前の、プロパティー、フィールド、メソッドが実行時の引数として利用される
  • 1つの関数に何回でもつけることができる
  • 対象となる関数には Theory 属性をつけないとテストは実行されない

メソッド版

  • 第1引数で指定した名前の static メンバー関数が実行されその結果がテストに渡される
  • 関数の戻り値は、イテレータブルなものである必要があり、その中身が順次テストの引数として渡される
  • 第2引数以降の引数は、ソースの生成用関数に渡される
  • 以下の様に、 IEnumerable<T>yield を利用すればきれいに書ける
public static IEnumerable<object[]> GetSource()
{
    yield return new object[] { 0, 0, 0 };
    yield return new object[] { 1, 0, 1 };
    yield return new object[] { 1, 1, 0 };
    yield return new object[] { 2, 1, 1 };
}

[Theory,
MemberData(nameof(GetSource))]
public void AddTest(int expected, int x, int y)
{
    Assert.Equal(expected, Add(x, y));
}

プロパティー版

  • 第1引数で指定した名前の static プロパティーの値がテストに渡される
  • 関数の戻り値は、イテレータブルなものである必要があり、その中身が順次テストの引数として渡される
public static object[][] Source
{
    get
    {
        return new object[][]
        {
            new object[] { 0, 0, 0 },
            new object[] { 1, 0, 1 },
            new object[] { 1, 1, 0 },
            new object[] { 2, 1, 1 },
        };
    }
}

[Theory,
MemberData(nameof(Source))]
public void AddTest(int expected, int x, int y)
{
    Assert.Equal(expected, Add(x, y));
}

フィールド版

  • 第1引数で指定した名前の static フィールドの値がテストに渡される
  • 関数の戻り値は、イテレータブルなものである必要があり、その中身が順次テストの引数として渡される
public static object[][] Source = new object[][]
{
    new object[] { 0, 0, 0 },
    new object[] { 1, 0, 1 },
    new object[] { 1, 1, 0 },
    new object[] { 2, 1, 1 },
}

[Theory,
MemberData(nameof(Source))]
public void AddTest(int expected, int x, int y)
{
    Assert.Equal(expected, Add(x, y));
}

ClassData 属性

TheoryAttributeClassDataAttribute を使って、ソースを持つ class を使用する。

public class Tests
{
    [Theory, ClassData(typeof(ParseIntSource))]
    public void ParseInt(int expected, string source)
    {
        Assert.Equal(expected, int.Parse(source));
    }
}

public class ParseIntSource : IEnumerable<object[]>
{
    private readonly List<object[]> _data = new List<object[]>
    {
        new object[] { 1, "1" },
        new object[] { 2, "2" },
    };

    public IEnumerator<object[]> GetEnumerator()
    { return _data.GetEnumerator(); }

    IEnumerator IEnumerable.GetEnumerator()
    { return GetEnumerator(); }
}

参考資料