Wie kann ich Moq anweisen, eine Aufgabe zurückzugeben?

327

Ich habe eine Schnittstelle, die deklariert

Task DoSomethingAsync();

Ich verwende MoqFramework für meine Tests:

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

Dann führe ich in meinem Test den Code aus, der aufgerufen wird await DoSomethingAsync(). Und der Test schlägt in dieser Zeile einfach fehl. Was mache ich falsch?

Waldemar
quelle
5
Wenn Sie die Testfehler in dieser Zeile sagen, welchen Fehler erzeugt er?
AlSki
@AlSki wahrscheinlich eine NullReferenceException. wie Sie hier sehen können
LuckyLikey

Antworten:

706

Ihre Methode hat keine Rückrufe, daher gibt es keinen Grund zur Verwendung .CallBack(). Sie können einfach eine Aufgabe mit den gewünschten Werten mit .Returns()und Task.FromResult zurückgeben , z.

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

Update 22.06.2014

Moq 4.2 verfügt über zwei neue Erweiterungsmethoden, die dies unterstützen.

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

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

Update 2016-05-05

Wie Seth Flowers in der anderen Antwort erwähnt , ReturnsAsyncist es nur für Methoden verfügbar, die a zurückgeben Task<T>. Für Methoden, die nur eine Aufgabe zurückgeben,

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

kann verwendet werden.

Wie in dieser Antwort gezeigt, wird dies in .NET 4.6 vereinfacht .Returns(Task.CompletedTask);, z.

mock.Setup(arg=>arg.DoSomethingAsync())        
    .Returns(Task.CompletedTask);
Panagiotis Kanavos
quelle
16
.Returns (Task.CompletedTask); das war meine Antwort
Todd Vance
8
Vielen Dank, dass Sie diese Antwort auf dem neuesten Stand halten, da das Moq-Framework aktualisiert wurde!
Jacob Stamm
.Returns(Task.FromResult(default(object))funktioniert gut, wenn der Rückgabetyp ungültig ist. .Returns(Task.FromResult(null as MyType))funktioniert gut, wenn der erwartete Rückgabetyp null ist.
Jeremy Ray Brown
1
@JeremyRayBrown, wie ich erkläre, wird in .NET 4.6 default(object)nicht mehr benötigt. null as MyTypeist dasselbe, default(MyType)solange MyTypees sich um einen Referenztyp handelt.
Panagiotis Kanavos
40

Ähnliches Problem

Ich habe eine Oberfläche, die ungefähr so ​​aussah:

Task DoSomething(int arg);

Symptome

Mein Komponententest ist fehlgeschlagen, als mein Dienst awaitedden Anruf an testete DoSomething.

Fix

Im Gegensatz zur akzeptierten Antwort können Sie .ReturnsAsync()Ihre Setup()dieser Methode in diesem Szenario nicht aufrufen , da die Methode nicht generische Task, sondern die nicht generische Methode zurückgibt Task<T>.

Sie können .Returns(Task.FromResult(default(object)))das Setup jedoch weiterhin verwenden, sodass der Test bestanden werden kann.

Seth Blumen
quelle
1
Nur ein Gedanke dazu: Wenn Sie eine nicht generische Aufgabe (nicht .net 4.6) zurückgeben müssen, würde ich die Rückgabe von Task.Delay (1) als eine einfache Möglichkeit betrachten, eine Aufgabe zurückzugeben. Sie können die Arbeit auch nachahmen, indem Sie das Zeitargument erhöhen.
Stevethethread
26

Sie müssen nur .Returns(Task.FromResult(0));nach dem Rückruf hinzufügen .

Beispiel:

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

Jetzt können Sie auch das Talentsoft.Moq.SetupAsync-Paket https://github.com/TalentSoft/Moq.SetupAsync verwenden

Was auf der Grundlage der hier gefundenen Antworten und der Ideen, die Moq vorgeschlagen, aber hier noch nicht umgesetzt wurden: https://github.com/moq/moq4/issues/384 , die Einrichtung asynchroner Methoden erheblich vereinfacht

Einige Beispiele aus früheren Antworten mit der SetupAsync-Erweiterung:

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