447 Stimmen

Wie kann ich Moq sagen, dass es eine Aufgabe zurückgeben soll?

Ich habe eine Schnittstelle, die deklariert

Task DoSomethingAsync();

Ich benutze das MoqFramework für meine Tests:

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

Dann in meinem Test führe ich den Code aus, der await DoSomethingAsync() aufruft. Und der Test scheitert einfach an dieser Stelle. Was mache ich falsch?

924voto

Panagiotis Kanavos Punkte 102979

Deine Methode hat keine Rückrufe, daher gibt es keinen Grund .CallBack() zu verwenden. Du kannst einfach eine Aufgabe mit den gewünschten Werten zurückgeben, indem du .Returns() und Task.FromResult verwendest, z.B.:

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

Update 2014-06-22

Moq 4.2 hat zwei neue Erweiterungsmethoden, um dabei zu helfen.

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

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

Update 2016-05-05

Wie Seth Flowers im anderen Antwort erwähnt, ist ReturnsAsync nur für Methoden verfügbar, die ein Task zurückgeben. Für Methoden, die nur ein Task zurückgeben, kann verwendet werden:

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

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

mock.Setup(arg=>arg.DoSomethingAsync())        
    .Returns(Task.CompletedTask);

44voto

Seth Flowers Punkte 8819

Ähnliches Problem

Ich hatte eine Schnittstelle, die ungefähr so aussah:

Task DoSomething(int arg);

Symptome

Mein Unittest ist fehlgeschlagen, als mein zu testender Dienst den Aufruf von DoSomething mit await ausgeführt hat.

Fix

Im Gegensatz zur akzeptierten Antwort können Sie in diesem Szenario nicht .ReturnsAsync() auf Ihrem Setup() dieser Methode aufrufen, da die Methode den nicht-generischen Task zurückgibt, anstatt Task.

Sie können jedoch immer noch .Returns(Task.FromResult(default(object))) auf dem Setup verwenden, um den Test bestehen zu lassen.

30voto

Diego Torres Punkte 1169

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

Beispiel:

mock.Setup(arg => arg.DoSomethingAsync())
    .Callback(() => {  })
    .Returns(Task.FromResult(0));

4voto

user9812476 Punkte 74

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

Das basiert auf den hier gefundenen Antworten und den Ideen, die Moq vorgeschlagen wurden, aber bisher noch nicht umgesetzt wurden: https://github.com/moq/moq4/issues/384, erleichtern erheblich die Einrichtung von asynchronen Methoden

Einige Beispiele, die in früheren Antworten mit der SetupAsync-Erweiterung durchgeführt wurden:

mock.SetupAsync(arg=>arg.DoSomethingAsync());
mock.SetupAsync(arg=>arg.DoSomethingAsync()).Callback(() => {  });
mock.SetupAsync(arg=>arg.DoSomethingAsync()).Throws(new InvalidOperationException());

0voto

Patrick McDonald Punkte 61826

Danke für die akzeptierte Antwort für eine sehr hilfreiche Lösung.

Ich habe zwei Erweiterungsmethoden hinzugefügt (für Task und ValueTask) zu unseren Projekten, um es für Personen, die daran gewöhnt sind, .ReturnsAsync bei allen asynchronen Setups aufzurufen, besser auffindbar zu machen.

public static class MoqExtensions
{
    public static IReturnsResult ReturnsAsync(this IReturns mock)
        where TMock : class
    {
        return mock.Returns(Task.CompletedTask);
    }

    public static IReturnsResult ReturnsAsync(this IReturns mock) 
        where TMock : class
    {
        return mock.Returns(ValueTask.CompletedTask);
    }
}

Es kann wie folgt aufgerufen werden:

mock.Setup(x => x.DoSomethingAsync())        
    .ReturnsAsync();

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X