Pouvez-vous m’aider à comprendre Moq Callback?

Utiliser Moq et regardé Callback mais je n’ai pas pu trouver un exemple simple pour comprendre comment l’utiliser.

Avez-vous un petit extrait de travail expliquant clairement comment et quand l’utiliser?

Difficile de battre https://github.com/Moq/moq4/wiki/Quickstart

Si ce n’est pas assez clair, j’appellerais ça un bogue doc …

EDIT: En réponse à votre clarification …

Pour chaque méthode Setup vous effectuez, vous devez indiquer des éléments tels que:

  • contraintes sur les intrants
  • la valeur de / façon dont la valeur de retour (s’il en existe une) doit être dérivée

Le mécanisme .Callback dit “Je ne peux pas le décrire tout de suite, mais lorsqu’un appel en forme se produit, rappelez-moi et je ferai ce qui doit être fait”. Dans le cadre de la même chaîne d’appels fluide, vous devez contrôler le résultat pour retourner (le cas échéant) via .Returns “. Dans les exemples QS, un exemple est qu’ils augmentent chaque fois la valeur renvoyée.

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 antipatternes des tests de logique conditionnelle), et s’il existe un moyen plus simple ou intégré utilisé de préférence.

La partie 3 de 4 de la série Moq de Justin Etheredge le couvre, et il y a un autre exemple de rappel ici

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(); DataEntity insertedEntity = null; mock.Setup(x => x.Insert(It.IsAny())).Returns(1) .Callback((DataEntity de) => insertedEntity = de); 

Syntaxe de méthode générique alternative:

 mock.Setup(x => x.Insert(It.IsAny())).Returns(1) .Callback(de => insertedEntity = de); 

Ensuite, vous pouvez tester quelque chose comme

 Assert.AreEqual("test", insertedEntity.Description, "Wrong Description"); 

Il existe deux types de rappel dans 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 callbacks, nous pouvons:

  1. inspecter les arguments de la méthode
  2. méthode de capture
  3. changer l’état contextuel

Callback est simplement un moyen d’exécuter n’importe quel code personnalisé lorsque vous appelez l’une des méthodes du simulacre. Voici un exemple simple:

 public interface IFoo { int Bar(bool b); } var mock = new Mock(); mock.Setup(mc => mc.Bar(It.IsAny())) .Callback(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. Supposons que vous attendiez des appels sur votre maquette, mais ils se produisent simultanément. Vous n’avez donc aucun moyen de savoir dans quel ordre ils seront appelés, mais vous voulez savoir si les appels attendus ont bien eu lieu (sans tenir compte de la commande). Vous pouvez faire quelque chose comme ça:

 var cq = new ConcurrentQueue(); mock.Setup(f => f.Bar(It.IsAny())).Callback(cq.Enqueue); Parallel.Invoke(() => mock.Object.Bar(true), () => mock.Object.Bar(false)); Console.WriteLine("Invocations: " + Ssortingng.Join(", ", cq)); // output: // Invocations: True, False 

BTW ne pas être confondu par la distinction ” Returns Returns ” et “after Returns ” trompeurs. C’est simplement une distinction technique de savoir si votre code personnalisé s’exécutera après que les Returns ont été évalués 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 annulée, vous ne pouvez même pas appeler Returns , mais 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, je l’ai utilisé pour effectuer une logique avant de lancer une exception. Par exemple, je devais stocker tous les objects passés à une méthode pour une vérification ultérieure et cette méthode (dans certains cas de test) devait générer une exception. L’appel de .Throws(...) sur Mock.Setup(...) remplace l’action Callback() et ne l’appelle jamais. Cependant, en lançant une exception dans le rappel, vous pouvez toujours effectuer toutes les tâches intéressantes offertes par un rappel et lancer une exception.