2012-06-27 5 views
17

Estoy intentando descubrir cómo aplicar la asincrónica y esperar palabras clave para mis pruebas de xUnit. Estoy usando xUnit 1.9 y Async CTP 1.3. Aquí está mi caso de pruebaxUnit y Moq no son compatibles con async: aguarde las palabras clave

tengo una interfaz que especifica llamada de un método asíncrono

public interface IDoStuffAsync 
{ 
    Task AnAsyncMethod(string value); 
} 

tengo una clase que consume la interfaz y llama al método asíncrono

public class UseAnAsyncThing 
{ 
    private readonly IDoStuffAsync _doStuffAsync; 

    public UseAnAsyncThing(IDoStuffAsync doStuffAsync) 
    { 
     _doStuffAsync = doStuffAsync; 
    } 

    public async Task DoThatAsyncOperation(string theValue) 
    { 
     await _doStuffAsync.AnAsyncMethod(theValue); 
    } 
} 

En mis pruebas me deseo comprobar que el método DoThatAsyncOperation llama al método con el valor correcto, así que me burlo de la interfaz y uso el Moq para verificar la llamada

[Fact] 
    public async void The_test_will_pass_even_though_it_should_fail() 
    { 
     var mock = new Mock<IDoStuffAsync>(); 
     var sut = new UseAnAsyncThing(mock.Object); 

     mock.Setup(x => x.AnAsyncMethod(It.IsAny<string>())); 

     await sut.DoThatAsyncOperation("test"); 

     // This won't throw a Moq.MockExcpetion so the test appears to pass 
     // However it does not run 
     mock.Verify(x => x.AnAsyncMethod("fail")); 
    } 

Esta prueba utiliza las palabras clave async y await. Cuando se ejecuta, pasa erróneamente ya que Moq debería afirmar que la verificación falla. Cualquier código después de la llamada a sut.DoThatAsyncOperation("test"); no se ejecuta

[Fact] 
    public void This_will_work_and_assert_the_reslt() 
    { 
     var mock = new Mock<IDoStuffAsync>(); 
     var sut = new UseAnAsyncThing(mock.Object); 

     mock.Setup(x => x.AnAsyncMethod(It.IsAny<string>())); 

     sut.DoThatAsyncOperation("test").ContinueWith(y => { }); 

     // This won't throw a Moq.MockExcpetion so the test appears to pass 
     // However it does not run 
     mock.Verify(x => x.AnAsyncMethod("fail")); 
    } 

Esta prueba está configurada sin las palabras clave esperar, y al asincrónicos y pasa bien.

¿Es este comportamiento esperado para xUnit y Moq?


actualización

Gracias por el comentario de Esteban I lograron fijar la primera prueba, haciendo dos cambios. La prueba ahora devuelve una tarea en lugar de vacía y la simulación también devuelve una tarea.

[Fact] 
    public async Task The_test_will_pass_even_though_it_should_fail() 
    { 
     var mock = new Mock<IDoStuffAsync>(); 
     var sut = new UseAnAsyncThing(mock.Object); 

     mock.Setup(x => x.AnAsyncMethod(It.IsAny<string>())).ReturnAsync(true); 

     await sut.DoThatAsyncOperation("test"); 

     // This now fails as it should 
     mock.Verify(x => x.AnAsyncMethod("fail")); 
    } 
+3

Gracias por el párrafo anterior actualización. TaskCompletionSource fue la clave que estaba buscando para burlarse de mi método asíncrono mediante MOQ. – Philippe

+0

En lugar de crear explícitamente un TaskCompletionSource simplemente podría utilizar Task.FromResult: 'mock.Setup (x => x.AnAsyncMethod (It.IsAny ())) devuelve (Task.FromResult (verdadero));' – sapphiremirage

+0

. También puede usar mock.Setup (x => x.AnAsyncMethod (It.IsAny ())). ReturnAsync (true); para evitar configurar la tarea –

Respuesta

14

cambiar el método de prueba de la unidad para volver Task en lugar de void, y debería funcionar. Soporte para pruebas de unidad async voidis being considered for a future release.

Describo con detalle why async unit tests don't work by default en mi blog. (Los ejemplos de mi blog usan MSTest, pero los mismos problemas existían en cada otro corredor de prueba, incluyendo xUnit anterior a 1.9).

+0

Gracias por la sugerencia, pero eso también falla. Esta vez arroja una NullRefException al ejecutar el método de burlarse. Supongo que el simulacro debe devolver una Tarea. –

+0

De hecho, eso me lleva a la respuesta. Ahora puedo hacer pasar la prueba devolviendo un TaskCompletionSource desde el simulacro. –

+5

También puede usar 'Task.FromResult' como una abreviatura de la misma cosa. –

3

Traté de usar el código de su 'Actualización', pero se detenía en el método asincrónico que me estaba burlando.

var tcs = new TaskCompletionSource<T>(); 
    tcs.SetResult(default(T)); 


    mock.Setup(x => x.AnAsyncMethod(It.IsAny<T>())).Returns(tcs.Task); 

Así que para arreglar que tenía que cambiar el método de 'Volver':

mock.Setup(x => x.AnAsyncMethod(It.IsAny<T>())).Returns(()=> { return tcs.Task; }); 
Cuestiones relacionadas