Как я могу сказать 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(). И тест просто не проходит на этой линии. Что я делаю не так?

Waldemar
источник
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());
user9812476
источник