Réponses:
Difficile à battre https://github.com/Moq/moq4/wiki/Quickstart
Si ce n'est pas assez clair, j'appellerais cela un bug de la documentation ...
EDIT: En réponse à votre clarification ...
Pour chaque méthode simulée que Setup
vous effectuez, vous devez indiquer des éléments tels que:
Le .Callback
mécanisme dit "Je ne peux pas le décrire pour le moment, mais quand un appel en forme de tel se produit, rappelez-moi et je ferai ce qui doit être fait". Dans le cadre de la même chaîne d'appels fluides, vous pouvez contrôler le résultat à retourner (le cas échéant) via .Returns
". Dans les exemples QS, un exemple est qu'ils font augmenter la valeur renvoyée à chaque fois.
En général, vous n'aurez pas besoin d'un mécanisme comme celui-ci très souvent (les modèles de test xUnit ont des termes pour les antipatterns de la logique conditionnelle dans les tests), et s'il existe un moyen plus simple ou intégré d'établir ce dont vous avez besoin, il devrait être utilisé de préférence.
La partie 3 sur 4 de la série Moq de Justin Etheredge le couvre, et il y a un autre exemple de rappels ici
Un exemple simple de rappel peut être trouvé à Utilisation des rappels avec la publication Moq .
Callback
n'a rien à voir avec la valeur de retour (à moins que vous ne la liez par le code). Fondamentalement, il s'assure que le rappel est appelé avant ou après chaque appel (selon que vous l'avez enchaîné avant ou après Returns
respectivement), clair et simple.
Voici un exemple d'utilisation d'un rappel pour tester une entité envoyée à un service de données qui gère une insertion.
var mock = new Mock<IDataService>();
DataEntity insertedEntity = null;
mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1)
.Callback((DataEntity de) => insertedEntity = de);
Syntaxe de méthode générique alternative:
mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1)
.Callback<DataEntity>(de => insertedEntity = de);
Ensuite, vous pouvez tester quelque chose comme
Assert.AreEqual("test", insertedEntity.Description, "Wrong Description");
It.Is<T>
dans un Mock.Verify
au lieu de joncher le test avec des températures. Mais +1 parce que je parie qu'il y a beaucoup de gens qui fonctionneront le mieux à partir d'un exemple.
Il existe deux types de Callback
Moq. L'une se produit avant le retour de l'appel; l'autre se produit après le retour de l'appel.
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}");
});
Dans les deux rappels, nous pouvons:
Callback
est simplement un moyen d'exécuter le code personnalisé de votre choix lorsqu'un appel est effectué vers l'une des méthodes du simulacre. Voici un exemple simple:
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
J'ai récemment rencontré un cas d'utilisation intéressant pour cela. Supposons que vous attendiez des appels à votre maquette, mais qu'ils se produisent simultanément. Vous n'avez donc aucun moyen de connaître l'ordre dans lequel ils seront appelés, mais vous voulez savoir que les appels que vous attendiez ont eu lieu (quel que soit l'ordre). Vous pouvez faire quelque chose comme ceci:
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
BTW ne vous laissez pas confondre par la distinction trompeuse «avant Returns
» et «après Returns
». Il s'agit simplement d'une distinction technique pour savoir si votre code personnalisé s'exécutera après Returns
avoir été évalué ou avant. Aux yeux de l'appelant, les deux s'exécuteront avant que la valeur ne soit renvoyée. En effet, si la méthode est de void
retour, vous ne pouvez même pas appeler Returns
et pourtant cela fonctionne de la même manière. Pour plus d'informations, consultez https://stackoverflow.com/a/28727099/67824 .
En plus des autres bonnes réponses ici, je l'ai utilisé pour exécuter la logique avant de lancer une exception. Par exemple, j'avais besoin de stocker tous les objets passés à une méthode pour une vérification ultérieure, et cette méthode (dans certains cas de test) avait besoin de lever une exception. L'appel .Throws(...)
sur Mock.Setup(...)
remplace l' Callback()
action et ne l'appelle jamais. Cependant, en lançant une exception dans le callback, vous pouvez toujours faire toutes les bonnes choses qu'un rappel a à offrir, et toujours lever une exception.