86 votes

SetupSequence dans Moq

Je veux un simulateur qui renvoie 0 la première fois, puis renvoie 1 à chaque fois que la méthode est appelée par la suite. Le problème est que si la méthode est appelée 4 fois, je dois écrire :

mock.SetupSequence(x => x.GetNumber())
    .Returns(0)
    .Returns(1)
    .Returns(1)
    .Returns(1);

Sinon, la méthode renvoie null.

Existe-t-il un moyen d'écrire que, après l'appel initial, la méthode renvoie 1 ?

1voto

Tikiman163 Points 31

Normalement, je ne prendrais pas la peine de soumettre une nouvelle réponse à une question aussi ancienne, mais ces dernières années, ReturnsAsync est devenu très courant, ce qui rend les réponses potentielles plus compliquées.

Comme d'autres l'ont dit, il suffit de créer une file d'attente de résultats et de passer le délégué queue.Dequeue dans votre appel de retour.

Eg.

var queue = new Queue<int>(new []{0,1,2,3});
mock.SetupSequence(m => m.Bar()).Returns(queue.Dequeue);

Cependant, si vous configurez une méthode asynchrone, nous devrions normalement appeler ReturnsAsync. queue.Dequeue lorsqu'il est passé dans ReturnsAsync et le premier appel à la méthode configurée fonctionnera correctement, mais les appels suivants lèveront une exception de référence nulle. Vous pourriez, comme certains des autres exemples l'ont fait, créer votre propre méthode d'extension qui renvoie une tâche, mais cette approche ne fonctionne pas avec SetupSequence, et vous devez utiliser Returns au lieu de ReturnsAsync. De plus, le fait de devoir créer une méthode d'extension pour gérer le retour des résultats va à l'encontre de l'objectif de l'utilisation de Moq en premier lieu. Et dans tous les cas, toute méthode dont le type de retour est Task et pour laquelle vous avez passé un délégué à Returns ou ReturnsAsync échouera toujours au deuxième appel lors de la mise en place via SetupSequence.

Il existe cependant deux alternatives amusantes à cette approche qui ne nécessitent qu'un minimum de code supplémentaire. La première option est de reconnaître que le Setup et le SetupAsync de l'objet Mock suivent les modèles de conception de Fluent Api. Ce que cela signifie, c'est que techniquement, Setup, SetupAsync, Returns et ReturnsAsync retournent tous un objet "Builder". Ce que j'appelle un objet de type Builder sont des objets de style Fluent Api comme QueryBuilder, StringBuilder, ModelBuilder et IServiceCollection/IServiceProvider. Le résultat pratique de tout ceci est que nous pouvons facilement faire ceci :

var queue = new List<int>(){0,1,2,3};
var setup = mock.SetupSequence(m => m.BarAsync());
foreach(var item in queue)
{
  setup.ReturnsAsync(item);
}

Cette approche nous permet d'utiliser à la fois SetupSequence et ReturnsAsync, ce qui, à mon avis, correspond au modèle de conception le plus intuitif.

La deuxième approche consiste à réaliser que Returns est capable d'accepter un délégué qui renvoie une tâche, et que Setup renverra toujours la même chose. Cela signifie que si nous devions créer une méthode d'extension pour Queue comme ceci :

public static class EMs
{
  public static async Task<T> DequeueAsync<T>(this Queue<T> queue)
  {
    return queue.Dequeue();
  }
}

Nous pourrions alors simplement écrire :

var queue = new Queue<int>(new []{0,1,2,3});
mock.Setup(m => m.BarAsync()).Returns(queue.DequeueAsync);

Nous pourrions aussi utiliser la classe AsyncQueue de Microsoft.VisualStudio.Threading, qui nous permettrait de le faire :

var queue = new AsyncQueue<int>(new []{0,1,2,3});
mock.Setup(m => m.BarAsync()).Returns(queue.DequeueAsync);

Le principal problème à l'origine de tout cela est que, lorsque la fin d'une séquence de configuration est atteinte, la méthode est traitée comme n'étant pas configurée. Pour éviter cela, vous êtes censé appeler également un Setup standard si les résultats doivent être renvoyés après que la fin de la séquence a été atteinte.

J'ai rédigé un fiddle assez complet concernant cette fonctionnalité avec des exemples d'erreurs que vous pouvez rencontrer en faisant les choses mal, ainsi que des exemples de plusieurs façons différentes de faire les choses correctement. https://dotnetfiddle.net/KbJlxb

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