Как я могу сказать Moq вернуть задание?


327

У меня есть интерфейс, который объявляет

Task DoSomethingAsync();

Я использую MoqFramework для своих тестов:

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

Затем в моем тесте я выполняю код, который вызывает await DoSomethingAsync(). И тест просто не проходит на этой линии. Что я делаю не так?


5
Когда вы говорите об ошибках теста в этой строке, какую ошибку это вызывает?
— AlSki

@AlSki, вероятно, исключение NullReferenceException. как вы можете видеть здесь
— LuckyLikey

Ответы:


709

Ваш метод не имеет обратных вызовов, поэтому нет причин для его использования .CallBack(). Вы можете просто вернуть Task с нужными значениями, используя .Returns()и Task.FromResult , например:

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

Обновление 2014-06-22

Moq 4.2 имеет два новых метода расширения, чтобы помочь с этим.

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

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

Обновление 2016-05-05

Как упоминает Сет Флауэрс в другом ответе , ReturnsAsyncдоступно только для методов, которые возвращают a Task<T>. Для методов, которые возвращают только Задачу,

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

может быть использован.

Как показано в этом ответе , в .NET 4.6 это упрощается .Returns(Task.CompletedTask);, например:

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

16
.Returns (Task.CompletedTask); это был мой ответ
— Тодд Вэнс

8
Спасибо за актуальность этого ответа, так как фреймворк Moq получил обновления!
— Джейкоб Стамм

.Returns(Task.FromResult(default(object))хорошо работает, когда возвращаемый тип void. .Returns(Task.FromResult(null as MyType))хорошо работает, когда ожидаемый тип возвращаемого значения равен нулю.
— Джереми Рэй Браун

1
@JeremyRayBrown, как я объясняю, в .NET 4.6 default(object)больше не нужен. null as MyTypeтакой же , как до default(MyType)тех пор , как MyTypeэто ссылочный тип.
— Панагиотис Канавос

40

Аналогичная проблема

У меня есть интерфейс, который выглядел примерно так:

Task DoSomething(int arg);

симптомы

Мой модульный тест не прошел, когда мой сервис тестировал awaitedвызов DoSomething.

Fix

В отличие от принятого ответа, вы не можете позвонить .ReturnsAsync()на свой контекстуальный Setup()этого метода в этом случае, так как метод возвращает необщего Task, а не Task<T>.

Тем не менее, вы все еще можете использовать .Returns(Task.FromResult(default(object)))в настройках, что позволяет пройти тест.


1
Подумайте только: если вам нужно вернуть неуниверсальную задачу (не .net 4.6), я бы рассмотрел возврат Task.Delay (1) как простой способ вернуть задачу. Вы также можете имитировать работу, увеличив аргумент времени.
— stevethethread

26

Вам нужно только добавить .Returns(Task.FromResult(0));после обратного вызова.

Пример:

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

4

Теперь вы также можете использовать пакет Talentsoft.Moq.SetupAsync https://github.com/TalentSoft/Moq.SetupAsync.

Которые на основе ответов, найденных здесь, и идей, предложенных Moq, но еще не реализованных здесь: https://github.com/moq/moq4/issues/384 , значительно упрощают настройку асинхронных методов.

Несколько примеров из предыдущих ответов, выполненных с расширением SetupAsync:

mock.SetupAsync(arg=>arg.DoSomethingAsync());
mock.SetupAsync(arg=>arg.DoSomethingAsync()).Callback(() => { <my code here> });
mock.SetupAsync(arg=>arg.DoSomethingAsync()).Throws(new InvalidOperationException());
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.