Wie kann ich eine Methode modifizieren, deren Signatur ein optionales Argument enthält, ohne es explizit anzugeben oder eine Überladung zu verwenden?

119

Gegeben die folgende Schnittstelle:

public interface IFoo
{
    bool Foo(string a, bool b = false);
}

Der Versuch, es mit Moq zu verspotten:

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>())).Returns(false);

gibt beim Kompilieren den folgenden Fehler aus:

Ein Ausdrucksbaum darf keinen Aufruf oder Aufruf enthalten, der optionale Argumente verwendet

Ich habe das oben erwähnte Problem als Erweiterung in der Liste der Probleme von Moq gefunden und es scheint der Version 4.5 zugewiesen zu sein (wann immer dies der Fall ist).

Meine Frage ist: Was soll ich tun, da das oben genannte Problem nicht in Kürze behoben wird? Sind meine Optionen nur, entweder den Standardwert des optionalen Parameters jedes Mal explizit festzulegen, wenn ich ihn verspotte (was den Punkt der Angabe eines Punktes überhaupt zunichte macht) oder eine Überladung ohne den Bool zu erzeugen (wie ich es getan hätte) vor C # 4)?

Oder hat jemand einen klügeren Weg gefunden, um dieses Problem zu lösen?

Appulus
quelle
5
Wäre es sinnvoll, nur It.IsAny <bool> () für den zweiten Parameter anzugeben?
Paul d'Aoust
Eineinhalb Jahre später ist dies immer noch wahr.
Mukus
@ Mukus, zögern Sie nicht, eine PR fallen zu lassen, Bruder.
IamDOM

Antworten:

91

Ich glaube, Ihre einzige Wahl im Moment ist es, den boolParameter explizit in das Setup für aufzunehmen Foo.

Ich denke nicht, dass es den Zweck der Angabe eines Standardwerts zunichte macht. Der Standardwert ist eine Annehmlichkeit für das Aufrufen von Code, aber ich denke, dass Sie in Ihren Tests explizit sein sollten. Angenommen, Sie könnten die Angabe des boolParameters weglassen. Was passiert, wenn in Zukunft jemand den Standardwert von bin ändert true? Dies führt zu fehlgeschlagenen Tests (und das zu Recht), die jedoch aufgrund der versteckten Annahme schwieriger zu beheben bsind false. Die explizite Angabe des boolParameters hat einen weiteren Vorteil: Sie verbessert die Lesbarkeit Ihrer Tests. Jemand, der sie durchläuft, wird schnell wissen, dass es eine FooFunktion gibt, die zwei Parameter akzeptiert. Das sind zumindest meine 2 Cent :)

Wenn Sie es jedes Mal angeben, wenn Sie es verspotten, duplizieren Sie keinen Code: Erstellen und / oder initialisieren Sie das Modell in einer Funktion, sodass Sie nur einen einzigen Änderungspunkt haben. Wenn Sie wirklich wollen, können Sie das offensichtliche Manko von Moq hier überwinden, indem Sie Foodie Parameter in diese Initialisierungsfunktion duplizieren :

public void InitFooFuncOnFooMock(Mock<IFoo> fooMock, string a, bool b = false)
{
    if(!b)
    {
        fooMock.Setup(mock => mock.Foo(a, b)).Returns(false);
    }
    else
    {
        ...
    }
}
Chris Mantle
quelle
1
Ausgezeichnete Antwort; Ich habe es bereits explizit in meinen Verspottungen angegeben, aber Ihre Antwort bestätigt auf sehr klare und logische Weise, warum ich es so machen sollte. Danke, @Chris.
Appulus
9
Das Ändern eines Standardparameters "sollte" Tests unterbrechen. Wenn Tests nicht fehlschlagen, wenn ein Standard geändert wird, kann dies ein Zeichen für schlechte Tests sein. Der Code kann die Standardeinstellungen verwenden, die Tests jedoch nicht?
Pop Catalin
Es ist schon eine Weile her, aber ich habe diesen Ansatz mit Moq versucht, als ich versucht habe, eine Schnittstelle (IDConnection in Dapper) zu verspotten, und ich erhalte immer noch den gleichen Fehler. Irgendwelche Ideen warum? Beispielzeile: mockDB.Setup (x => x.Query <MyObject> (It.IsAny <string> (), It.IsAny <DynamicParameters> (), It.IsAny <IDbTransaction> (), false, 600)). Rückgabe (neue Liste <MyObject> ()); Die letzten beiden Werte sind die optionalen Parameter der von mir eingerichteten Methode.
Raelshark
4
Arrgggh! Das schreckliche if (!x) {} else {}Anti-Muster :)
nicodemus13
1
@ nicodemus13 Ja, aber ich habe versucht, das Codebeispiel so nah wie möglich am Beispiel des OP in der Frage zu halten. Ich würde es nicht unbedingt befürworten :)
Chris Mantle
8

Moq ist heute auf dieses Problem gestoßen und unterstützt diesen Anwendungsfall nicht. Es scheint also, dass das Überschreiben der Methode für diesen Fall ausreichend wäre.

public interface IFoo
{
    bool Foo(string a);

    bool Foo(string a, bool b);
}

Jetzt sind beide Methoden verfügbar und dieses Beispiel würde funktionieren:

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>())).Returns(false);
Gil Grossman
quelle
2

Mit Moq Version 4.10.1 konnte ich Folgendes tun

Mit Schnittstelle:

public interface IFoo
{
    bool Foo(string a, bool b = false);
}

Und verspotten

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>(), It.IsAny<bool>())).Returns(false);

Löst einen Aufruf von Foo mit dem ersten Parameter in Ordnung auf

CF5
quelle