¿Cómo puedo decirle a Moq que devuelva una tarea?

327

Tengo una interfaz que declara

Task DoSomethingAsync();

Estoy usando MoqFramework para mis pruebas:

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

Luego en mi prueba ejecuto el código que invoca await DoSomethingAsync(). Y la prueba simplemente falla en esa línea. ¿Qué estoy haciendo mal?

Waldemar
fuente
55
Cuando dice los errores de prueba en esa línea, ¿qué error produce?
AlSki
@AlSki posiblemente una NullReferenceException. como puedes ver aquí
LuckyLikey

Respuestas:

709

Su método no tiene devoluciones de llamada, por lo que no hay razón para usarlo .CallBack(). Simplemente puede devolver una tarea con los valores deseados usando .Returns()y Task.FromResult , por ejemplo:

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

Actualización 2014-06-22

Moq 4.2 tiene dos nuevos métodos de extensión para ayudar con esto.

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

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

Actualizar 2016-05-05

Como Seth Flowers menciona en la otra respuesta , ReturnsAsyncsolo está disponible para los métodos que devuelven a Task<T>. Para los métodos que devuelven solo una tarea,

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

puede ser usado.

Como se muestra en esta respuesta , en .NET 4.6 esto se simplifica a .Returns(Task.CompletedTask);, por ejemplo:

mock.Setup(arg=>arg.DoSomethingAsync())        
    .Returns(Task.CompletedTask);
Panagiotis Kanavos
fuente
16
.Returns (Task.CompletedTask); esa fue mi respuesta
Todd Vance
8
¡Gracias por mantener esta respuesta actualizada ya que el marco Moq ha recibido actualizaciones!
Jacob Stamm
.Returns(Task.FromResult(default(object))funciona bien cuando el tipo de retorno es nulo. .Returns(Task.FromResult(null as MyType))funciona bien cuando el tipo de retorno esperado es nulo.
Jeremy Ray Brown
1
@JeremyRayBrown como explico, en .NET 4.6 default(object)ya no es necesario. null as MyTypees el mismo que default(MyType)el tiempo que MyTypees un tipo de referencia.
Panagiotis Kanavos
40

Problema similar

Tengo una interfaz que se parecía más o menos a:

Task DoSomething(int arg);

Síntomas

La prueba de mi unidad falló cuando mi servicio bajo prueba fue awaitedllamado DoSomething.

Reparar

A diferencia de la respuesta aceptada, no puede invocar .ReturnsAsync()su Setup()método en este escenario, porque el método devuelve el no genérico Task, en lugar de Task<T>.

Sin embargo, aún puede usarlo .Returns(Task.FromResult(default(object)))en la configuración, lo que permite que la prueba pase.

Seth Flowers
fuente
1
Solo una idea al respecto, si necesita devolver una tarea no genérica (no .net 4.6), consideraría devolver Task.Delay (1) como una manera fácil de devolver una tarea. También puede imitar el trabajo aumentando el argumento del tiempo.
stevethethread
26

Solo necesita agregar .Returns(Task.FromResult(0));después de la devolución de llamada.

Ejemplo:

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

Ahora también puede usar el paquete Talentsoft.Moq.SetupAsync https://github.com/TalentSoft/Moq.SetupAsync

En la base de las respuestas encontradas aquí y las ideas propuestas a Moq pero aún no implementadas aquí: https://github.com/moq/moq4/issues/384 , simplifica enormemente la configuración de los métodos asíncronos

Pocos ejemplos encontrados en respuestas anteriores realizadas con la extensión SetupAsync:

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