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?


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);

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.


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));

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());
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.