¿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?


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);

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.


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));

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());
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.