Halcyon Days

IT × 移住 × ファイナンス

MENU

テストヘルパークラスを作ったら「System.NotSupportedException : Specified method is not supported.」というエラーが発生した話

表題の通りなのですが……。

System.NotSupportedException : Specified method is not supported. というエラーが発生して詰まった話をば。

エラーが発生した経緯

C#APIの機能追加を行ったので追加分のテストコードを書いていたときにエラーが発生。

やったこと

エラーが発生するまでにやったことは以下の通り。

テストケースが15個ほどあり、それぞれのケースに対してダミーデータを作成しなければならなかった。

しかし、ケースごとのダミーデータを作成しているとテストコード全体が非常に長くなってしまう。

そこでダミーデータを定義するテストヘルパーを作成して対応することにした。

最初は他のテストコードに倣い、同じファイル内でダミーデータの定義を行っていたが、この時はエラーが発生しなかった。

サンプルコード

まずはイメージしやすくするためにサンプルコードを書いてみる。

エラーが発生する前は下記のように同じファイル内でダミーデータを作成していた。

public class SampleTests
{
    public static object trueData => new object[]
    {
        new object[] { 0001, "x", "y" },
        new object[] { 0002, "x", "z" },
        new object[] { 0003, "y", "z" }
    };

    [Theory]
    [MemberData(nameof(trueData))]
    public async Task GetData(int id, string param1, string param2)
    {
        // arrange
        var databaseValue = await Database.GetDataByIdAsync(id);

        // assert
        Assert.Equal(param1, databaseValue.Param1);
        Assert.Equal(param2, databaseValue.Param2);
    }
}

「ダミーデータがくっそ多くなるなぁ……」と思ったのでデータの作成をヘルパークラスに移した。

public static class TestDataHelper
{
    public static object GetTrueData()
    {
        return new object[]
        {
            new object[] { 0001, "x", "y" },
            new object[] { 0002, "x", "z" },
            new object[] { 0003, "y", "z" }
        };
    }
}

その後、テストコードを下記のように修正した。

public class SampleTests
{
    [Theory]
    [MemberData(nameof(TestDataHelper.GetTrueData))]
    public async Task GetData(int id, string param1, string param2)
    {
        // arrange
        var databaseValue = await Database.GetDataByIdAsync(id);

        // assert
        Assert.Equal(param1, databaseValue.Param1);
        Assert.Equal(param2, databaseValue.Param2);
    }
}

これでテストを実行すると System.NotSupportedException : Specified method is not supported. というエラーが発生するようになった。なんでや。

エラー原因は何か?

エラー原因は2つ。

1つは [MemberData(nameof(TestDataHelper.GetTrueData))] の部分。

もう1つは GetTrueData の型が間違っているため。

それぞれ見ていく。

なぜ [MemberData(nameof(TestDataHelper.GetTrueData))] だとだめなのか?

下記の3つがダメな理由。

  • MemberData Attributeは静的なプロパティやフィールドにアクセスしなければならない
  • 別のクラスからデータ読み込む場合は MemberType Attributeを使って型を指定しなければならない

なので、テストコードを下記のように修正した。

public class SampleTests
{
    [Theory]
    [MemberData(nameof(TestDataHelper.GetTrueData), MemberType = typeof(TestDataHelper))]
    public async Task GetData(int id, string param1, string param2)
    {
        // arrange
        var databaseValue = await Database.GetDataByIdAsync(id);

        // assert
        Assert.Equal(param1, databaseValue.Param1);
        Assert.Equal(param2, databaseValue.Param2);
    }
}

テストコードに MemberType = typeof(TestDataHelper) を追加して、別のクラスから型を指定してデータを読み込むように変更。

GetTrueData の型は何が正しいのか?

MemberData で読み込まれるデータの型は IEnumerable<object[]> でなければならないらしい。

object のままだとxUnitの MemberData の型と一致しないためにエラーになってしまう。

そこで、 GetTrueData の型を object から IEnumerable<object[]> に変更。

public static class TestDataHelper
{
    public static IEnumerable<object[]> GetTrueData()
    {
        return new object[]
        {
            new object[] { 0001, "x", "y" },
            new object[] { 0002, "x", "z" },
            new object[] { 0003, "y", "z" }
        };
    }
}

すっきり書きたいなら下記ようにするといいかも。

public static class TestDataHelper
{
    public static IEnumerable<object[]> GetTrueData()
    {
        yield return new object[] { 0001, "x", "y" },
      yield return new object[] { 0002, "x", "z" },
      yield return new object[] { 0003, "y", "z" }
    }
}

これでテストを再実行するとテストがパスするようになった。

System.NotSupportedException : Specified method is not supported. 」で調べてもあまり記事が出てこなかったので焦った……。

理屈を知ればなんてことはなかった。

参照ドキュメント