表題の通りなのですが……。
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.
」で調べてもあまり記事が出てこなかったので焦った……。
理屈を知ればなんてことはなかった。