Ich habe eine Methode, die so aussieht:
private async void DoStuff(long idToLookUp)
{
IOrder order = await orderService.LookUpIdAsync(idToLookUp);
// Close the search
IsSearchShowing = false;
}
//Other stuff in case you want to see it
public DelegateCommand<long> DoLookupCommand{ get; set; }
ViewModel()
{
DoLookupCommand= new DelegateCommand<long>(DoStuff);
}
Ich versuche es wie folgt zu testen:
[TestMethod]
public void TestDoStuff()
{
//+ Arrange
myViewModel.IsSearchShowing = true;
// container is my Unity container and it setup in the init method.
container.Resolve<IOrderService>().Returns(orderService);
orderService = Substitute.For<IOrderService>();
orderService.LookUpIdAsync(Arg.Any<long>())
.Returns(new Task<IOrder>(() => null));
//+ Act
myViewModel.DoLookupCommand.Execute(0);
//+ Assert
myViewModel.IsSearchShowing.Should().BeFalse();
}
Meine Behauptung wird aufgerufen, bevor ich mit dem verspotteten LookUpIdAsync fertig bin. In meinem normalen Code ist das genau das, was ich will. Aber für meinen Unit-Test will ich das nicht.
Ich konvertiere von BackgroundWorker nach Async / Await. Mit Background Worker funktionierte dies korrekt, da ich warten konnte, bis der BackgroundWorker fertig war.
Aber es scheint keine Möglichkeit zu geben, auf eine asynchrone Methode zu warten ...
Wie kann ich diese Methode testen?
quelle
await
der Aufgabe werden Ausnahmen beachtet . Wenn Sie es ohne aufrufenawait
, werden alle Fehler stillschweigend ignoriert.Eine
async void
Methode ist im Wesentlichen eine "Feuer und Vergessen" -Methode. Es gibt keine Möglichkeit, ein Abschlussereignis zurückzugewinnen (ohne ein externes Ereignis usw.).Wenn Sie dies einem Unit-Test unterziehen müssen, würde ich empfehlen, es
async Task
stattdessen zu einer Methode zu machen. Sie können dannWait()
die Ergebnisse aufrufen , die Sie benachrichtigen, wenn die Methode abgeschlossen ist.Diese Testmethode, wie sie geschrieben wurde, würde jedoch immer noch nicht funktionieren, da Sie nicht
DoStuff
direkt testen , sondern eine testen,DelegateCommand
die sie umschließt. Sie müssten diese Methode direkt testen.quelle
async Task
statt machenasync void
und auf die Aufgabe warten ...async void
Methoden haben (außer für Event-Handler).async void
Wie werden Sie damit umgehen, wenn in der Methode eine Ausnahme ausgelöst wird?Ich habe einen Weg gefunden, dies für Unit-Tests zu tun:
[TestMethod] public void TestDoStuff() { //+ Arrange myViewModel.IsSearchShowing = true; // container is my Unity container and it setup in the init method. container.Resolve<IOrderService>().Returns(orderService); orderService = Substitute.For<IOrderService>(); var lookupTask = Task<IOrder>.Factory.StartNew(() => { return new Order(); }); orderService.LookUpIdAsync(Arg.Any<long>()).Returns(lookupTask); //+ Act myViewModel.DoLookupCommand.Execute(0); lookupTask.Wait(); //+ Assert myViewModel.IsSearchShowing.Should().BeFalse(); }
Der Schlüssel hier ist, dass ich, weil ich Unit-Tests bin, die Aufgabe ersetzen kann, bei der mein asynchroner Aufruf (innerhalb meiner asynchronen Leere) zurückgegeben werden soll. Ich stelle dann einfach sicher, dass die Aufgabe abgeschlossen ist, bevor ich weitermache.
quelle
lookupTask
fertig sind, heißt das nicht, dass die zu testende Methode (DoStuff
? OderDoLookupCommand
?) Fertig ist. Es besteht eine geringe Wahrscheinlichkeit, dass die Aufgabe ausgeführt wurde, aberIsSearchShowing
noch nicht auf false gesetzt wurde. In diesem Fall würde Ihre Behauptung fehlschlagen.Thread.Sleep(2000)
vor dem SetzenIsSearchShowing
auf false zu setzen.Der einzige Weg, den ich kenne, besteht darin, Ihre
async void
Methode inasync Task
Methode umzuwandelnquelle
Sie können ein AutoResetEvent verwenden, um die Testmethode anzuhalten, bis der asynchrone Aufruf abgeschlossen ist:
[TestMethod()] public void Async_Test() { TypeToTest target = new TypeToTest(); AutoResetEvent AsyncCallComplete = new AutoResetEvent(false); SuccessResponse SuccessResult = null; Exception FailureResult = null; target.AsyncMethodToTest( (SuccessResponse response) => { SuccessResult = response; AsyncCallComplete.Set(); }, (Exception ex) => { FailureResult = ex; AsyncCallComplete.Set(); } ); // Wait until either async results signal completion. AsyncCallComplete.WaitOne(); Assert.AreEqual(null, FailureResult); }
quelle
Die bereitgestellte Antwort testet den Befehl und nicht die asynchrone Methode. Wie oben erwähnt, benötigen Sie einen weiteren Test, um auch diese asynchrone Methode zu testen.
Nachdem ich einige Zeit mit einem ähnlichen Problem verbracht hatte, fand ich eine einfache Wartezeit, um eine asynchrone Methode in einem Komponententest zu testen, indem ich einfach synchron aufrief:
protected static void CallSync(Action target) { var task = new Task(target); task.RunSynchronously(); }
und die Verwendung:
Der Test wartet in dieser Zeile und wird fortgesetzt, nachdem das Ergebnis fertig ist, sodass wir sofort danach bestätigen können.
quelle
Ändern Sie Ihre Methode, um eine Aufgabe zurückzugeben, und Sie können Task.Result verwenden
bool res = configuration.InitializeAsync(appConfig).Result; Assert.IsTrue(res);
quelle
configuration
?Ich hatte ein ähnliches Problem. In meinem Fall bestand die Lösung darin,
Task.FromResult
im moq-Setup Folgendes zu verwenden.Returns(...)
:orderService.LookUpIdAsync(Arg.Any<long>()) .Returns(Task.FromResult(null));
Alternativ hat Moq auch eine
ReturnsAysnc(...)
Methode.quelle