webentwicklung-frage-antwort-db.com.de

Moq: So gelangen Sie zu einem Parameter, der an eine Methode eines gespielten Dienstes übergeben wurde

Stellen Sie sich diese Klasse vor

public class Foo {

    private Handler _h;

    public Foo(Handler h)
    {
        _h = h;
    }

    public void Bar(int i)
    {
        _h.AsyncHandle(CalcOn(i));
    }

    private SomeResponse CalcOn(int i)
    {
        ...;
    }
}

Mo (q) cking Handler in einem Test von Foo, wie könnte ich überprüfen, was Bar() an _h.AsyncHandle Übergeben hat?

142
Jan

Sie können die Mock.Callback-Methode verwenden:

var mock = new Mock<Handler>();
SomeResponse result = null;
mock.Setup(h => h.AnsyncHandle(It.IsAny<SomeResponse>()))
    .Callback<SomeResponse>(r => result = r);

// do your test
new Foo(mock.Object).Bar(22);
Assert.NotNull(result);

Wenn Sie nur das übergebene Argument überprüfen möchten, können Sie dies auch direkt tun:

mock.Setup(h => h.AnsyncHandle(It.Is<SomeResponse>(response => response != null)));
241
Gamlor

Gamlors Antwort funktioniert, aber eine andere Methode (und eine, die ich im Test für aussagekräftiger halte) ist ...

var mock = new Mock<Handler>();
var desiredParam = 47; // this is what you want to be passed to AsyncHandle
new Foo(mock.Object).Bar(22);
mock.Verify(h => h.AsyncHandle(desiredParam), Times.Once());

Verify ist sehr leistungsfähig und es lohnt sich, sich daran zu gewöhnen.

19
Pete Martin

Gamlors Antwort funktionierte für mich, aber ich dachte, ich würde den Kommentar von John Carpenter erweitern, weil ich nach einer Lösung mit mehr als einem Parameter suchte. Ich nahm an, dass andere Leute, die über diese Seite stolpern, möglicherweise in einer ähnlichen Situation sind. Ich fand diese Information in der Moq-Dokumentation .

Ich werde Gamlors Beispiel verwenden, aber wir wollen so tun, als würde die AsyncHandle-Methode zwei Argumente annehmen: ein string und ein SomeResponse -Objekt.

var mock = new Mock<Handler>();
string stringResult = string.Empty;
SomeResponse someResponse = null;
mock.Setup(h => h.AsyncHandle(It.IsAny<string>(), It.IsAny<SomeResponse>()))
    .Callback<string, SomeResponse>((s, r) => 
    {
        stringResult = s;
        someResponse = r;
    });

// do your test
new Foo(mock.Object).Bar(22);
Assert.AreEqual("expected string", stringResult);
Assert.IsNotNull(someResponse);

Grundsätzlich müssen Sie nur einen weiteren It.IsAny<>() mit dem entsprechenden Typ hinzufügen, der Callback -Methode einen weiteren Typ hinzufügen und den Lambda-Ausdruck entsprechend ändern.

17
JavaJudt

Die Callback-Methode wird sicherlich funktionieren, aber wenn Sie dies bei einer Methode mit vielen Parametern tun, kann dies etwas ausführlich sein. Hier ist etwas, das ich verwendet habe, um etwas von der Kesselplatte zu entfernen.

var mock = new Mock<Handler>();

// do your test   
new Foo(mock.Object).Bar(22);

var arg = new ArgumentCaptor<SomeResponse>();
mock.Verify(h => h.AsyncHandle(arg.Capture()));
Assert.NotNull(arg.Value);

Hier ist die Quelle für ArgumentCaptor:

public class ArgumentCaptor<T>
{
    public T Capture()
    {
        return It.Is<T>(t => SaveValue(t));
    }

    private bool SaveValue(T t)
    {
        Value = t;
        return true;
    }

    public T Value { get; private set; }
}
14
Andrew Radford

Sie könnten It.Is<TValue>() matcher verwenden.

var mock = new Mock<Handler>();
new Foo(mock.Object).Bar(22);
mock.Verify(h => h.AsyncHandle(It.Is<SomeResponse>(r => r != null )));
4

Die Alternative ist auch Capture.In Funktion von moq. Es ist die Funktion OOTB moq, die die Argumenterfassung in der Auflistung ermöglicht.

//Arrange
var args = new List<SomeResponse>();
mock.Setup(h => h.AnsyncHandle(Capture.In(args)));

//Act
new Foo(mock.Object).Bar(22);

//Assert
//... assert args.Single() or args.First()
2
Johnny

Das funktioniert auch:

Mock<InterfaceThing> mockedObject = new Mock<InterfaceThing>();
var objectParameter = mockedObject.Invocations[1].Arguments[0] as ObjectParameter;
1
Jeff Smith

Viele gute Antworten hier! Entscheiden Sie sich für das sofort einsatzbereite Moq-Feature-Set, bis Sie Aussagen zu mehreren Klassenparametern treffen müssen, die an Ihre Abhängigkeiten übergeben wurden. Wenn Sie jedoch in dieser Situation enden, kann die Moq Verify-Funktion mit It.Is-Matchern den Testfehler nicht gut eingrenzen, und die Rückgabe/Rückruf-Methode zum Erfassen von Argumenten fügt Ihrem Test unnötige Codezeilen hinzu (und lange Tests sind für mich ein No-Go).

Hier ist ein Gist: https://Gist.github.com/Jacob-McKay/8b8d41ebb9565f5fca23654fd944ac6b mit einer Erweiterung von Moq (4.12), die eine aussagekräftigere Möglichkeit bietet, Aussagen über Argumente zu machen, die an Mocks weitergegeben wurden , ohne die vorgenannten Nachteile. So sieht der Überprüfungsabschnitt jetzt aus:

        mockDependency
            .CheckMethodWasCalledOnce(nameof(IExampleDependency.PersistThings))
            .WithArg<InThing2>(inThing2 =>
            {
                Assert.Equal("Input Data with Important additional data", inThing2.Prop1);
                Assert.Equal("I need a trim", inThing2.Prop2);
            })
            .AndArg<InThing3>(inThing3 =>
            {
                Assert.Equal("Important Default Value", inThing3.Prop1);
                Assert.Equal("I NEED TO BE UPPER CASED", inThing3.Prop2);
            });

Ich wäre begeistert, wenn Moq eine Funktion zur Verfügung stellen würde, die das Gleiche leistet, gleichzeitig deklarativ ist und die Fehlerisolierung bietet, die dies bewirkt. Daumen drücken!

0
Jacob McKay