134 votes

Comment moquer une méthode dont la signature contient un argument facultatif sans le spécifier explicitement ou en utilisant une surcharge ?

Étant donné l'interface suivante :

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

J'essaie de le simuler 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 :

Un arbre d'expression ne peut pas contenir d'appel ou d'invocation utilisant des arguments facultatifs.

J'ai trouvé la question ci-dessus soulevée en tant que amélioration dans la liste des problèmes de Moq et il semble qu'elle soit assignée à la version 4.5 (quelle qu'elle soit).

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

Ou bien quelqu'un a-t-il trouvé un moyen plus astucieux de résoudre ce problème ?

5 votes

Serait-il raisonnable de spécifier simplement It.IsAny<bool>() pour le second paramètre ?

0 votes

Un an et demi plus tard, c'est toujours vrai

0 votes

@Mukus, n'hésite pas à laisser un PR, bro.

98voto

Chris Mantle Points 3601

Je crois que votre seul choix pour l'instant est d'inclure explicitement l' bool dans la configuration pour Foo .

Je ne pense pas que cela aille à l'encontre de l'objectif de spécifier une valeur par défaut. La valeur par défaut est une commodité pour appeler le code, mais je pense que vous devriez être explicite dans vos tests. Disons que vous pourriez ne pas spécifier la valeur bool paramètre. Que se passe-t-il si, à l'avenir, quelqu'un modifie la valeur par défaut du paramètre b à true ? Cela conduira à l'échec des tests (et à juste titre), mais ils seront plus difficiles à corriger à cause de l'hypothèse cachée que b est false . En spécifiant explicitement le bool a un autre avantage : il améliore la lisibilité de vos tests. Quelqu'un qui les parcourt saura rapidement qu'il y a un paramètre Foo qui accepte deux paramètres. C'est du moins ce que je pense :)

Pour ce qui est de le spécifier à chaque fois que vous le simulez, ne dupliquez pas le code : créez et/ou initialisez le simulateur dans une fonction, afin de n'avoir qu'un seul point de changement. Si vous le voulez vraiment, vous pouvez surmonter le défaut apparent de Moq en dupliquant Foo dans 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
    {
        ...
    }
}

1 votes

Excellente réponse ; j'ai déjà pris les devants et l'ai spécifié explicitement dans mes tests de simulation, mais votre réponse confirme de manière très claire et logique pourquoi je dois procéder de cette façon. Merci, @Chris.

9 votes

La modification d'un paramètre par défaut "devrait" interrompre les tests. Le fait que les tests n'échouent pas lorsqu'un paramètre par défaut est modifié peut être un signe de mauvais tests. Le code peut utiliser les paramètres par défaut mais les tests ne le font pas ?

0 votes

Cela fait un moment, mais j'ai essayé cette approche avec Moq en essayant de simuler une interface (IDConnection dans Dapper) et j'obtiens toujours la même erreur. Une idée de la raison ? Exemple de ligne : mockDB.Setup(x => x.Query<MyObject>(It.IsAny<string>(), It.IsAny<DynamicParameters>(), It.IsAny<IDbTransaction>(), false, 600)).Returns(new List<MyObject>()) ; Les deux dernières valeurs sont les paramètres optionnels de la méthode que je configure.

9voto

Gil Grossman Points 139

Je viens de rencontrer ce problème aujourd'hui, Moq ne supporte pas ce cas d'utilisation. Il semble donc que le remplacement de la méthode soit suffisant 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);

9voto

CF5 Points 559

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

Avec interface :

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

Et se moquer

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.

0voto

EJoshuaS Points 7022

Dans mon cas, mon problème était que j'ai oublié d'inclure un It.IsAny<string>() pour l'un de mes paramètres optionnels. Une fois que j'ai eu un It.Is... pour tous mes paramètres (y compris les paramètres facultatifs), la compilation s'est déroulée sans problème.

0 votes

Compiler bien n'est pas la même chose que d'être réellement surchargé en méthode fantaisie.

-2voto

Sofia Khwaja Points 41

Une solution simple pourrait être de modifier le code pour ajouter une valeur fausse pour l'appel Mock

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X