Jak mogę powiedzieć Moq, aby zwrócił zadanie?

327

Mam interfejs, który deklaruje

Task DoSomethingAsync();

Używam MoqFramework do moich testów:

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

Następnie w teście wykonuję kod, który się wywołuje await DoSomethingAsync(). A test po prostu kończy się niepowodzeniem na tej linii. Co ja robię źle?

Waldemar
źródło
5
Kiedy mówisz o błędach testowych w tym wierszu, jaki to błąd?
AlSki
@AlSki prawdopodobnie NullReferenceException. jak widać tutaj
LuckyLikey

Odpowiedzi:

709

Twoja metoda nie ma żadnych wywołań zwrotnych, więc nie ma powodu, aby jej używać .CallBack(). Możesz po prostu zwrócić zadanie z żądanymi wartościami za pomocą .Returns()i Task.FromResult , np .:

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

Aktualizacja 22.06.2014

Moq 4.2 ma dwie nowe metody rozszerzenia, aby w tym pomóc.

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

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

Aktualizacja 2016-05-05

Jak wspomina Seth Flowers w drugiej odpowiedzi , ReturnsAsyncjest dostępna tylko dla metod, które zwracają a Task<T>. W przypadku metod, które zwracają tylko Zadanie,

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

może być użyty.

Jak pokazano w tej odpowiedzi , w .NET 4.6 jest to uproszczone .Returns(Task.CompletedTask);, np .:

mock.Setup(arg=>arg.DoSomethingAsync())        
    .Returns(Task.CompletedTask);
Panagiotis Kanavos
źródło
16
.Returns (Task.CompletedTask); to była moja odpowiedź
Todd Vance
8
Dziękujemy za aktualizację tej odpowiedzi, ponieważ ramy Moq otrzymały aktualizacje!
Jacob Stamm,
.Returns(Task.FromResult(default(object))działa dobrze, gdy typ zwrotu jest nieważny. .Returns(Task.FromResult(null as MyType))działa dobrze, gdy oczekiwany typ zwrotu jest pusty.
Jeremy Ray Brown
1
@JeremyRayBrown, jak wyjaśniam, w .NET 4.6 default(object)nie jest już potrzebny. null as MyTypejest taki sam, jak default(MyType)długo MyTypejest typem odniesienia.
Panagiotis Kanavos,
40

Podobny problem

Mam interfejs, który wyglądał mniej więcej tak:

Task DoSomething(int arg);

Objawy

Mój test jednostkowy nie powiódł się, gdy testowana usługa awaiteddzwoniła do DoSomething.

Naprawić

W przeciwieństwie do akceptowanego odpowiedź, nie jesteś w stanie zadzwonić .ReturnsAsync()na Twoim Setup()tej metody w tym scenariuszu, ponieważ metoda zwraca nierodzajową Taskzamiast Task<T>.

Nadal możesz jednak korzystać .Returns(Task.FromResult(default(object)))z konfiguracji, co pozwala na zaliczenie testu.

Kwiaty Setha
źródło
1
Wystarczy pomyśleć o tym, jeśli chcesz zwrócić zadanie inne niż ogólne (nie .net 4.6), rozważam zwrócenie zadania Task.Delay (1) jako łatwego sposobu zwrotu zadania. Możesz także naśladować pracę, zwiększając argument czasu.
stevetheth 18.04.16
26

Musisz dodać tylko .Returns(Task.FromResult(0));po oddzwonieniu.

Przykład:

mock.Setup(arg => arg.DoSomethingAsync())
    .Callback(() => { <my code here> })
    .Returns(Task.FromResult(0));
Diego Torres
źródło
4

Teraz możesz także korzystać z pakietu Talentsoft.Moq.SetupAsync https://github.com/TalentSoft/Moq.SetupAsync

Które na podstawie odpowiedzi tutaj znalezionych i pomysłów zaproponowanych Moq, ale wciąż jeszcze nie zaimplementowanych tutaj: https://github.com/moq/moq4/issues/384 , znacznie upraszczają konfigurację metod asynchronicznych

Kilka przykładów znalezionych w poprzednich odpowiedziach wykonanych przy użyciu rozszerzenia SetupAsync:

mock.SetupAsync(arg=>arg.DoSomethingAsync());
mock.SetupAsync(arg=>arg.DoSomethingAsync()).Callback(() => { <my code here> });
mock.SetupAsync(arg=>arg.DoSomethingAsync()).Throws(new InvalidOperationException());
użytkownik9812476
źródło