Antworten:
Schwer zu schlagen https://github.com/Moq/moq4/wiki/Quickstart
Wenn das nicht klar genug ist, würde ich das einen Doc-Fehler nennen ...
EDIT: Als Antwort auf Ihre Klarstellung ...
Für jede verspottete Methode, die Setup
Sie ausführen, können Sie Folgendes angeben:
Der .Callback
Mechanismus sagt: "Ich kann es momentan nicht beschreiben, aber wenn ein Anruf in dieser Form auftritt, rufen Sie mich zurück und ich werde tun, was getan werden muss." Als Teil derselben fließenden Aufrufkette können Sie das Ergebnis steuern, das (falls vorhanden) über .Returns
"zurückgegeben werden soll. In den QS-Beispielen wird beispielsweise der zurückgegebene Wert jedes Mal erhöht.
Im Allgemeinen benötigen Sie einen solchen Mechanismus nicht sehr oft (xUnit-Testmuster enthalten Begriffe für Antimuster der ilk Conditional Logic In Tests), und wenn es eine einfachere oder integrierte Methode gibt, um festzustellen, was Sie benötigen, sollte dies der Fall sein bevorzugt verwendet.
Teil 3 von 4 in Justin Etheredges Moq-Serie behandelt es, und hier gibt es ein weiteres Beispiel für Rückrufe
Ein einfaches Beispiel für einen Rückruf finden Sie unter Verwenden von Rückrufen mit Moq- Post.
Callback
hat nichts mit dem Rückgabewert zu tun (es sei denn, Sie verknüpfen es zufällig über Code). Grundsätzlich wird nur sichergestellt, dass der Rückruf vor oder nach jedem Aufruf aufgerufen wird (je nachdem, ob Sie ihn vor oder nach dem Aufruf verkettet Returns
haben).
Hier ist ein Beispiel für die Verwendung eines Rückrufs zum Testen einer Entität, die an einen Datendienst gesendet wurde, der eine Einfügung verarbeitet.
var mock = new Mock<IDataService>();
DataEntity insertedEntity = null;
mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1)
.Callback((DataEntity de) => insertedEntity = de);
Alternative generische Methodensyntax:
mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1)
.Callback<DataEntity>(de => insertedEntity = de);
Dann können Sie so etwas testen
Assert.AreEqual("test", insertedEntity.Description, "Wrong Description");
It.Is<T>
in einem zu verwenden, Mock.Verify
anstatt den Test mit Zeitarbeitskräften zu verschmutzen. Aber +1, weil ich wette, dass es viele Leute gibt, die anhand eines Beispiels am besten funktionieren.
Es gibt zwei Arten von Callback
in Moq. Eine passiert, bevor der Anruf zurückkehrt; Das andere geschieht, nachdem der Anruf zurückgekehrt ist.
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}");
});
In beiden Rückrufen können wir:
Callback
ist einfach ein Mittel, um einen beliebigen benutzerdefinierten Code auszuführen, wenn eine der Methoden des Mocks aufgerufen wird. Hier ist ein einfaches Beispiel:
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
Ich bin kürzlich auf einen interessanten Anwendungsfall gestoßen. Angenommen, Sie erwarten einige Anrufe bei Ihrem Mock, die jedoch gleichzeitig stattfinden. Sie haben also keine Möglichkeit, die Reihenfolge zu kennen, in der sie angerufen werden, aber Sie möchten wissen, welche Anrufe Sie erwartet haben (unabhängig von der Reihenfolge). Sie können so etwas tun:
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
Übrigens, lassen Sie sich nicht durch die irreführende Unterscheidung "vor Returns
" und "nach Returns
" verwirren . Es ist lediglich eine technische Unterscheidung, ob Ihr benutzerdefinierter Code nach Returns
oder vor der Auswertung ausgeführt wird. In den Augen des Anrufers werden beide ausgeführt, bevor der Wert zurückgegeben wird. In der Tat, wenn die Methode void
zurückkehrt, können Sie nicht einmal aufrufen Returns
und dennoch funktioniert es genauso. Weitere Informationen finden Sie unter https://stackoverflow.com/a/28727099/67824 .
Zusätzlich zu den anderen guten Antworten hier habe ich damit Logik ausgeführt, bevor eine Ausnahme ausgelöst wurde. Zum Beispiel musste ich alle Objekte speichern, die zur späteren Überprüfung an eine Methode übergeben wurden, und diese Methode musste (in einigen Testfällen) eine Ausnahme auslösen. Der Aufruf .Throws(...)
an Mock.Setup(...)
Überschreibungen der Callback()
Aktion und nie nennt. Wenn Sie jedoch eine Ausnahme innerhalb des Rückrufs auslösen, können Sie immer noch alle guten Dinge tun, die ein Rückruf zu bieten hat, und dennoch eine Ausnahme auslösen.