web-dev-qa-db-ja.com

式は、モックされたオブジェクトに属さないメソッドを参照しています

別のAPIサービスを呼び出すAPIサービスがあります。 Mockオブジェクトをセットアップすると、エラーが発生して失敗しました。

NotSupportedException:式は、モックされたオブジェクトに属さないメソッドを参照しています。

これはコードです:

_private Mock<IEnumerable<ICarrierApiService<AccountSearchModel>>> _mockCarrierService;
private Mock<IApiService<AccountSearchModel>> _mockApiService;

[SetUp]
public void SetUp()
{
  _mockApiService = new Mock<IApiService<AccountSearchModel>>();
  _mockCarrierService = new Mock<IEnumerable<ICarrierApiService<AccountSearchModel>>>();
  _mockApiService.Setup(x => x.GetFromApiWithQuery(It.IsAny<string>())).Returns(ApiValue());

  // Error occurred when call _mockApiService.GetFromApiWithQuery() in .Select()
  _mockCarrierService.Setup(x => x
            .Select(s => s
                .GetFromApiWithQuery(It.IsAny<string>())).ToList())
                .Returns(new List<IQueryable<AccountSearchModel>> { ApiValue() });
}
_

Moqでの表現テスト と読みましたが、私の場合はうまくいきませんでした。この_mockCarrierService.Setup()を削除すると、テストケースは実行できますが、有効な_List<IQueryable<AccountSearchModel>>_が設定されていないため、NullReferenceExceptionで失敗します。

これを達成する方法はありますか?


脚注:現在の解決策

FWIW、ここに私が現在使用しているソリューションがあります。 私は問題へのより良いアプローチのためのすべての耳です(Moqがモッキング拡張メソッドのサポートを開始するまで)。

_private List<ICarrierApiService<AccountSearchModel>> _mockCarrierService;
private AccountSearchController _mockController;
private Mock<ICarrierApiService<AccountSearchModel>> _mockApiService;

[SetUp]
public void SetUp()
{
   _mockApiService = new Mock<ICarrierApiService<AccountSearchModel>>();
   _carrierServiceMocks = new List<ICarrierApiService<AccountSearchModel>> { _mockApiService.Object };
   _mockApiService.Setup(x => x.GetFromApiWithQuery(It.IsAny<string>())).Returns(ApiValue());
   _mockController = new AccountSearchController(_carrierServiceMocks);
}
_

脚注:代替のモックフレームワーク

また、モック拡張メソッドとハウツードキュメントへのリンクをサポートする商用のモックフレームワークを見つけました: Telerik JustMock

38
display name

この問題は、IEnumerable<T>のインスタンスメソッドではなく、 拡張メソッド であるSelectメソッドをモックしようとしているために発生します。

基本的に、拡張メソッドをモックする方法はありません。 この質問 をご覧ください。役に立つと思われるアイデアがいくつかあります。

UPD(2014年12月11日):

拡張メソッドのモックについての理解を深めるには、次のことを考慮してください。

  • 拡張メソッドは、拡張型のインスタンスメソッドであるかのように呼び出されますが、実際には、わずかな構文糖を持つ静的メソッドです。

  • System.Linq名前空間の拡張メソッドは、 純粋な関数 として実装されます。これらは決定論的であり、観察可能な 副作用 はありません。 純粋な関数であるメソッドを除き、静的メソッドは悪です —このステートメントにも同意することを願っています:)

  • それでは、タイプTのオブジェクトが与えられた場合、静的純関数f(T obj)をどのように実装しますか?オブジェクトT(または実際には他の純関数)に対して定義されている他の純関数を結合するか、不変で決定論的なグローバル状態を読み取る(関数f決定論と副作用なし)。実際、「不変で決定論的なグローバル状態」には、より便利な名前、つまり定数があります。

したがって、静的メソッドは純粋な関数でなければならないというルールに従うと(少なくともLINQメソッドではMicrosoftがこのルールに従うように見える)、拡張メソッドf(this T obj)拡張メソッドで使用される非静的メソッドまたは状態のモックに還元できるはずです—その拡張メソッドはobjインスタンスメソッドと状態に依存しているためその実装(および場合によっては他の純関数および/または定数値)。

IEnumerable<T>の場合、Select()拡張メソッドは、GetEnumerator()メソッドを使用するforeachステートメントに関して 実装済み です。したがって、GetEnumerator()をモックして、それに依存する拡張メソッドに必要な動作を実現できます。

66
Sergey Kolodiy

あなたが持っている:

__mockCarrierService = new Mock<IEnumerable<ICarrierApiService<AccountSearchModel>>>();
_

あなたは_IEnumerable<>_をモックします。 _IEnumerable<>_が持つ唯一のメンバーは、メソッドGetEnumerator()(さらに、ベースインターフェースから継承された同じシグネチャGetEnumerator()を持つ別のメソッド)です。 Selectメソッドは、(最初​​の回答で指摘したように)拡張メソッドであり、GetEnumerator()を呼び出すことで動作する静的メソッドです(C#foreachステートメントを使用することもあります) )。

モックでSetup of GetEnumeratorを実行することで、物事を機能させることができます。

ただし、_IEnumerable<>_のような_List<>_である「具体的な」非モック型を単純に使用する方がはるかに簡単です。だから試してください:

__mockCarrierService = new List<ICarrierApiService<AccountSearchModel>>();
_

次に、_List<>_にエントリを追加します。追加する必要があるのは、GetFromApiWithQueryメソッドが設定されている_Mock<ICarrierApiService<AccountSearchModel>>_です。

7

また、IConfigurationをモックする必要がある場合は、以下のコードを使用できます。

var builder = new ConfigurationBuilder()
        .AddInMemoryCollection(new Dictionary<string, string>
        {
            { "your-key", "your value" }
        });
        var config = builder.Build();
0