Comment dire à Moq de retourner une tâche?

327

J'ai une interface qui déclare

Task DoSomethingAsync();

J'utilise MoqFramework pour mes tests:

[TestMethod()]
public async Task MyAsyncTest()
{
   Mock<ISomeInterface> mock = new Mock<ISomeInterface>();
   mock.Setup(arg => arg.DoSomethingAsync()).Callback(() => { <my code here> });
   ...
}

Ensuite, dans mon test, j'exécute le code qui invoque await DoSomethingAsync(). Et le test échoue simplement sur cette ligne. Qu'est-ce que je fais mal?

Waldemar
la source
5
Lorsque vous dites les erreurs de test sur cette ligne, quelle erreur cela produit-il?
AlSki
@AlSki est probablement une NullReferenceException. comme vous pouvez le voir ici
LuckyLikey

Réponses:

709

Votre méthode n'a pas de rappel, il n'y a donc aucune raison de l'utiliser .CallBack(). Vous pouvez simplement renvoyer une tâche avec les valeurs souhaitées en utilisant .Returns()et Task.FromResult , par exemple:

MyType someValue=...;
mock.Setup(arg=>arg.DoSomethingAsync())        
    .Returns(Task.FromResult(someValue));

Mise à jour 2014-06-22

Moq 4.2 a deux nouvelles méthodes d'extension pour vous aider.

mock.Setup(arg=>arg.DoSomethingAsync())
    .ReturnsAsync(someValue);

mock.Setup(arg=>arg.DoSomethingAsync())        
    .ThrowsAsync(new InvalidOperationException());

Mise à jour 2016-05-05

Comme le mentionne Seth Flowers dans l' autre réponse , ReturnsAsyncn'est disponible que pour les méthodes qui renvoient a Task<T>. Pour les méthodes qui renvoient uniquement une tâche,

.Returns(Task.FromResult(default(object)))

peut être utilisé.

Comme indiqué dans cette réponse , dans .NET 4.6, cela est simplifié .Returns(Task.CompletedTask);, par exemple:

mock.Setup(arg=>arg.DoSomethingAsync())        
    .Returns(Task.CompletedTask);
Panagiotis Kanavos
la source
16
.Returns (Task.CompletedTask); c'était ma réponse
Todd Vance
8
Merci d'avoir gardé cette réponse à jour car le framework Moq a reçu des mises à jour!
Jacob Stamm
.Returns(Task.FromResult(default(object))fonctionne bien lorsque le type de retour est nul. .Returns(Task.FromResult(null as MyType))fonctionne bien lorsque le type de retour attendu est nul.
Jeremy Ray Brown
1
@JeremyRayBrown comme je l'explique, dans .NET 4.6 default(object)n'est plus nécessaire. null as MyTypeest le même tant default(MyType)qu'il MyTypes'agit d'un type de référence.
Panagiotis Kanavos
40

Problème similaire

J'ai une interface qui ressemble à peu près à:

Task DoSomething(int arg);

Symptômes

Mon test unitaire a échoué lorsque mon service sous test a awaitedappelé DoSomething.

Réparer

Contrairement à la réponse acceptée, vous ne pouvez pas faire appel .ReturnsAsync()à votre Setup()de cette méthode dans ce scénario, car la méthode renvoie le non générique Taskplutôt que Task<T>.

Cependant, vous pouvez toujours l'utiliser .Returns(Task.FromResult(default(object)))sur la configuration, ce qui permet au test de passer.

Fleurs de Seth
la source
1
Juste une pensée à ce sujet, si vous devez retourner une tâche non générique (non .net 4.6), je considérerais le retour de Task.Delay (1) comme un moyen facile de retourner une tâche. Vous pouvez également imiter le travail en augmentant l'argument temps.
stevethethread le
26

Vous devez seulement ajouter .Returns(Task.FromResult(0));après le rappel.

Exemple:

mock.Setup(arg => arg.DoSomethingAsync())
    .Callback(() => { <my code here> })
    .Returns(Task.FromResult(0));
Diego Torres
la source
4

Vous pouvez désormais également utiliser le package Talentsoft.Moq.SetupAsync https://github.com/TalentSoft/Moq.SetupAsync

Lequel sur la base des réponses trouvées ici et des idées proposées à Moq mais toujours pas encore implémentées ici: https://github.com/moq/moq4/issues/384 , simplifie grandement la configuration des méthodes asynchrones

Quelques exemples trouvés dans les réponses précédentes faites avec l'extension SetupAsync:

mock.SetupAsync(arg=>arg.DoSomethingAsync());
mock.SetupAsync(arg=>arg.DoSomethingAsync()).Callback(() => { <my code here> });
mock.SetupAsync(arg=>arg.DoSomethingAsync()).Throws(new InvalidOperationException());
user9812476
la source