Moqコールバックを理解するのを手伝ってくれませんか?


95

Moqを使用して確認しましたCallbackが、使用方法を理解するための簡単な例を見つけることができませんでした。

いつどのように使用するかを明確に説明する小さなスニペットはありますか?

回答:


82

打ちにくいhttps://github.com/Moq/moq4/wiki/Quickstart

それが十分に明確でない場合は、それをドキュメントのバグと呼びます...

編集:あなたの説明に応じて...

Setup実行するモックメソッドごとに、次のようなことを示すことができます。

  • 入力の制約
  • 戻り値(存在する場合)が導出される方法/の値

この.Callbackメカニズムでは、「現在は説明できませんが、このような形の通話が発生したら、折り返し電話してください。必要な処理を行います」と書かれています。同じ流暢な呼び出しチェーンの一部として、返される結果(ある場合)を.Returns" を介して制御できます。QSの例では、返される値が毎回増加する例がQSの例です。

一般に、このようなメカニズムはあまり必要ありません(xUnitテストパターンには、テスト中のilk条件付きロジックのアンチパターンの用語があります)。必要なものを確立するためのより簡単なまたは組み込みの方法がある場合は、優先的に使用されます。

Justin EtheredgeのMoqシリーズの4のパート3はそれをカバーし、ここにコールバックの別の例があります

コールバックの簡単な例は、Moqでのコールバックの使用に関する投稿にあります。


3
こんにちはルーベン私はMoqを学んでいます。もしあなたが好きなら、それを使って物事を行う方法を理解するためにたくさんの例を集めています。私の問題は、いつそれを使用するのかわからないことです。問題が解決したことを理解したら、独自のコードを記述します。コールバックを使用するのはいつですか。あなたの時間に感謝します
user9969

15
[リンク]に勝てませんか?どういたしまして。このリンクは、何十ものさまざまなことを行う方法を示していますが、なぜそれらのいずれかを行う必要があるのか​​については説明していません。これは、ドキュメントのモック作成でよくある問題です。私が見つけたTDD +モッキングの良い明確な説明の数は、ゼロフィンガーで数えることができます。ほとんどの人は、知識があれば、その記事を読む必要がないと思います。
ライアンランディ

@Kyralessa:私はあなたの主張を理解します。私は個人的にかなりの本の知識を持っているので、クイックスタートのものは絶対的に完璧だと思いました。残念ながら、投稿の最後にリンクした例よりも良い例は知りません。見つけた場合は、ここに投稿してください。編集できます(またはDIY
でもかまいません

「必要な処理を実行して、返される結果(あれば)を伝えます」これは誤解を招くと思います。AFAIU Callbackは戻り値とは何の関係もありません(コードを介してリンクする場合を除きます)。基本的に、コールバックが各呼び出しの前または後に呼び出されることを確認するだけです(それぞれ前または後のどちらにチェーンしたかによって異なりますReturns)。
Ohad Schneider 2017

1
@OhadSchneider私のリンクをたどって...あなたは正しいです!Fluentインターフェースが変更された(つまり、誤った仮定をして、通常はうまくいくので、リンク先の情報を読み取らなかった)場合は、不思議(ただし、Moqを長い間使用していなかったため、あまり関心がありません)オートコンプリート自分自身から)。修正があなたの要点を解決することを願って、それが解決しない場合は私に知らせてください
Ruben Bartelink 2017

59

以下は、コールバックを使用して、挿入を処理するデータサービスに送信されたエンティティをテストする例です。

var mock = new Mock<IDataService>();
DataEntity insertedEntity = null;

mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1) 
           .Callback((DataEntity de) => insertedEntity = de);

代替のジェネリックメソッド構文:

mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1) 
           .Callback<DataEntity>(de => insertedEntity = de);

次に、次のようなものをテストできます

Assert.AreEqual("test", insertedEntity.Description, "Wrong Description");

4
おそらくその特定のケースでは(状態または動作に対してテストを表現しようとしているかどうかに応じて)、tempsでテストを散らかす代わりにIt.Is<T>in を使用する方がきれいな場合がありますMock.Verify。しかし、+ 1は、例から最もうまくいく人がたくさんいると思うので。
Ruben Bartelink 2012年

10

CallbackMoqには2つのタイプがあります。1つは呼び出しが戻る前に発生します。もう1つは、呼び出しが戻った後に発生します。

var message = "";
mock.Setup(foo => foo.Execute(arg1: "ping", arg2: "pong"))
    .Callback((x, y) =>
    {
        message = "Rally on!";
        Console.WriteLine($"args before returns {x} {y}");
    })
    .Returns(message) // Rally on!
    .Callback((x, y) =>
    {
        message = "Rally over!";
        Console.WriteLine("arg after returns {x} {y}");
    });

両方のコールバックで、次のことができます。

  1. メソッド引数を検査する
  2. メソッド引数をキャプチャする
  3. コンテキストの状態を変更する

2
実際には、どちらも(呼び出し元に関する限り)呼び出しが戻る前に発生します。stackoverflow.com/a/28727099/67824を参照してください。
Ohad Schneider 2017

5

Callbackモックのメソッドの1つが呼び出されたときに、必要なカスタムコードを実行するための手段です。以下に簡単な例を示します。

public interface IFoo
{
    int Bar(bool b);
}

var mock = new Mock<IFoo>();

mock.Setup(mc => mc.Bar(It.IsAny<bool>()))
    .Callback<bool>(b => Console.WriteLine("Bar called with: " + b))
    .Returns(42);

var ret = mock.Object.Bar(true);
Console.WriteLine("Result: " + ret);

// output:
// Bar called with: True
// Result: 42

最近、興味深いユースケースに遭遇しました。モックへのいくつかの呼び出しを期待しているが、それらは同時に発生するとします。したがって、それらが呼び出される順序を知る方法はありませんが、期待どおりの呼び出しが行われたことを(順序に関係なく)知りたいと考えています。あなたはこのようなことをすることができます:

var cq = new ConcurrentQueue<bool>();
mock.Setup(f => f.Bar(It.IsAny<bool>())).Callback<bool>(cq.Enqueue);
Parallel.Invoke(() => mock.Object.Bar(true), () => mock.Object.Bar(false));
Console.WriteLine("Invocations: " + String.Join(", ", cq));

// output:
// Invocations: True, False

ところで、誤解を招く「前Returns」と「後Returns」の区別によって混乱することはありません。これは、カスタムコードがReturns評価された後または前に実行されるかどうかの技術的な違いにすぎません。呼び出し元の目には、値が返される前に両方が実行されます。実際、メソッドがvoid-returningの場合、呼び出すこともできませんがReturns、同じように機能します。詳細については、https://stackoverflow.com/a/28727099/67824を参照してください


1

ここで他の良い答えに加えて、私はそれを使用して、例外をスローする前にロジックを実行しました。たとえば、後で検証するためにメソッドに渡されたすべてのオブジェクトを格納する必要があり、そのメソッド(一部のテストケースでは)は例外をスローする必要がありました。を呼び出す.Throws(...)Mock.Setup(...)Callback()アクションがオーバーライドされ、呼び出されることはありません。ただし、コールバック内で例外をスローすることにより、コールバックが提供しなければならないすべてのことを実行でき、さらに例外をスローできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.