Comment puis-je Moq une méthode qui a un argument facultatif dans sa signature sans le spécifier explicitement ou en utilisant une surcharge?

119

Compte tenu de l'interface suivante:

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

Tentative de se moquer en utilisant Moq:

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

donne l'erreur suivante au moment de la compilation:

Une arborescence d'expression ne peut pas contenir un appel ou une invocation qui utilise des arguments facultatifs

J'ai trouvé le problème ci-dessus soulevé comme une amélioration dans la liste des problèmes de Moq et il semble être attribué à la version 4.5 (quand c'est le cas).

Ma question est la suivante: que dois-je faire étant donné que ce qui précède ne sera pas corrigé de sitôt? Mes options sont-elles uniquement pour définir explicitement la valeur par défaut du paramètre facultatif à chaque fois que je me moque de lui (ce qui va à l'encontre du point d'en spécifier un en premier lieu) ou pour créer une surcharge sans le booléen (comme ce que j'aurais fait avant C # 4)?

Ou est-ce que quelqu'un a trouvé un moyen plus intelligent de résoudre ce problème?

Appulus
la source
5
Serait-il raisonnable de spécifier simplement It.IsAny <bool> () pour le deuxième paramètre?
Paul d'Aoust
Un an et demi plus tard, c'est toujours vrai.
Mukus
@Mukus, n'hésitez pas à laisser tomber un PR, frérot.
IamDOM

Réponses:

91

Je pense que votre seul choix pour le moment est d'inclure explicitement le boolparamètre dans la configuration pourFoo .

Je ne pense pas que cela va à l'encontre de l'objectif de spécifier une valeur par défaut. La valeur par défaut est pratique pour appeler du code, mais je pense que vous devriez être explicite dans vos tests. Supposons que vous puissiez omettre de spécifier le boolparamètre. Que se passe-t-il si, à l'avenir, quelqu'un change la valeur par défaut de ben true? Cela conduira à des tests échoués (et à juste titre), mais ils seront plus difficiles à corriger en raison de l'hypothèse cachée qui best false. La spécification explicite du boolparamètre présente un autre avantage: elle améliore la lisibilité de vos tests. Quelqu'un qui les traversera saura rapidement qu'il y en a unFoo fonction qui accepte deux paramètres. C'est mes 2 cents, au moins :)

Quant à le spécifier à chaque fois que vous le moquez, ne dupliquez pas le code: créez et / ou initialisez le simulacre dans une fonction, de sorte que vous n'ayez qu'un seul point de changement. Si vous le souhaitez vraiment, vous pouvez ici surmonter le manque apparent de Moq en dupliquant Fooles paramètres de cette fonction d'initialisation:

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
la source
1
Excellente réponse; Je suis déjà allé de l'avant et l'ai spécifié explicitement dans mes moquettes, mais votre réponse confirme de manière très claire et logique pourquoi je devrais le faire de cette façon. Merci, @Chris.
Appulus
9
la modification d'un paramètre par défaut "devrait" interrompre les tests. Le fait de ne pas faire échouer les tests lorsqu'une valeur par défaut est modifiée peut être le signe d'un mauvais test. Le code peut utiliser les valeurs par défaut mais les tests ne le font pas?
Pop Catalin
Cela fait un moment, mais j'ai essayé cette approche avec Moq en essayant de me moquer d'une interface (IDConnection dans Dapper) et j'obtiens toujours la même erreur. Des idées pourquoi? Exemple de ligne: mockDB.Setup (x => x.Query <MyObject> (It.IsAny <string> (), It.IsAny <DynamicParameters> (), It.IsAny <IDbTransaction> (), false, 600)). Renvoie (new List <MyObject> ()); Les deux dernières valeurs sont les paramètres facultatifs de la méthode que je configure.
Raelshark
4
Arrgggh! The horrible if (!x) {} else {}anti-pattern :)
nicodemus13
1
@ nicodemus13 Oui, mais j'essayais de garder l'exemple de code aussi proche que possible de l'exemple de l'OP dans la question. Je ne le recommanderais pas nécessairement :)
Chris Mantle
8

Je viens de rencontrer ce problème aujourd'hui, Moq ne prend pas en charge ce cas d'utilisation. Donc, il semble que la substitution de la méthode serait suffisante dans ce cas.

public interface IFoo
{
    bool Foo(string a);

    bool Foo(string a, bool b);
}

Maintenant, les deux méthodes sont disponibles et cet exemple fonctionnerait:

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

En utilisant la version 4.10.1 de Moq, j'ai pu faire ce qui suit

Avec interface:

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

Et Mock

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

Résout un appel à Foo avec le premier paramètre ok

CF5
la source