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.