Como posso dizer ao Moq para retornar uma tarefa?

327

Eu tenho uma interface que declara

Task DoSomethingAsync();

Estou usando o MoqFramework para meus testes:

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

Então, no meu teste, eu executo o código que chama await DoSomethingAsync(). E o teste simplesmente falha nessa linha. O que estou fazendo de errado?

Waldemar
fonte
5
Quando você diz os erros de teste nessa linha, que erro ele produz?
precisa saber é o seguinte
@AlSki é propensamente uma NullReferenceException. como você pode ver aqui
LuckyLikey

Respostas:

709

Seu método não possui retornos de chamada, portanto não há motivo para usá-lo .CallBack(). Você pode simplesmente retornar uma tarefa com os valores desejados usando .Returns()e Task.FromResult , por exemplo:

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

Atualização 22/06/2014

O Moq 4.2 possui dois novos métodos de extensão para ajudar nisso.

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

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

Atualização 2016-05-05

Como Seth Flowers menciona na outra resposta , ReturnsAsyncsó está disponível para métodos que retornam a Task<T>. Para métodos que retornam apenas uma tarefa,

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

pode ser usado.

Conforme mostrado nesta resposta , no .NET 4.6, isso é simplificado para .Returns(Task.CompletedTask);, por exemplo:

mock.Setup(arg=>arg.DoSomethingAsync())        
    .Returns(Task.CompletedTask);
Panagiotis Kanavos
fonte
16
.Returns (Task.CompletedTask); essa foi a minha resposta #
6606 Todd Vance
8
Obrigado por manter esta resposta atualizada, pois a estrutura do Moq recebeu atualizações!
Jacob Stamm
.Returns(Task.FromResult(default(object))funciona bem quando o tipo de retorno é nulo. .Returns(Task.FromResult(null as MyType))funciona bem quando o tipo de retorno esperado é nulo.
Jeremy Ray Brown
1
@JeremyRayBrown, como explico, no .NET 4.6 default(object)não é mais necessário. null as MyTypeé o mesmo default(MyType)desde que MyTypeseja um tipo de referência.
Panagiotis Kanavos
40

Problema semelhante

Eu tenho uma interface que se parecia com:

Task DoSomething(int arg);

Sintomas

Meu teste de unidade falhou quando meu serviço testou awaiteda chamada para DoSomething.

Consertar

Ao contrário da resposta aceita, você é incapaz de chamar .ReturnsAsync()em sua Setup()deste método neste cenário, porque o método retorna o não-genérico Task, em vez de Task<T>.

No entanto, você ainda pode usar .Returns(Task.FromResult(default(object)))a configuração, permitindo que o teste seja aprovado.

Seth Flowers
fonte
1
Apenas um pensamento sobre isso, se você precisar retornar uma tarefa não genérica (não .net 4.6), consideraria o retorno de Task.Delay (1) como uma maneira fácil de retornar uma tarefa. Você também pode imitar o trabalho aumentando o argumento do tempo.
stevethethread
26

Você só precisa adicionar .Returns(Task.FromResult(0));após o retorno de chamada.

Exemplo:

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

Agora você também pode usar o pacote Talentsoft.Moq.SetupAsync https://github.com/TalentSoft/Moq.SetupAsync

Que, com base nas respostas encontradas aqui e nas idéias propostas ao Moq, mas ainda não implementadas aqui: https://github.com/moq/moq4/issues/384 , simplifica bastante a configuração dos métodos assíncronos

Poucos exemplos encontrados em respostas anteriores feitas com a extensão SetupAsync:

mock.SetupAsync(arg=>arg.DoSomethingAsync());
mock.SetupAsync(arg=>arg.DoSomethingAsync()).Callback(() => { <my code here> });
mock.SetupAsync(arg=>arg.DoSomethingAsync()).Throws(new InvalidOperationException());
user9812476
fonte