2010-06-03 12 views
10

Estoy considerando reescribir algunos de mis controladores MVC para que sean controladores asíncronos. Tengo pruebas de unidad de trabajo para estos controladores, pero estoy tratando de entender cómo mantenerlos en un entorno de controlador asíncrono.Prueba de unidades de construcción para Controladores asíncronos MVC2

Por ejemplo, actualmente tienen una acción como esta:

public ContentResult Transaction() 
{ 
    do stuff... 
    return Content("result"); 
} 

y mi unidad de prueba, básicamente se parece a:

var result = controller.Transaction(); 
Assert.AreEqual("result", result.Content); 

Ok, eso es bastante fácil.

Pero cuando cambia su controlador a este aspecto:

public void TransactionAsync() 
{ 
    do stuff... 
    AsyncManager.Parameters["result"] = "result"; 
} 

public ContentResult TransactionCompleted(string result) 
{ 
    return Content(result); 
} 

¿Cómo cree que sus pruebas de unidad debe construirse? Por supuesto, puede invocar el método de iniciador asíncrono en su método de prueba, pero ¿cómo obtiene el valor devuelto?

No he visto nada sobre esto en Google ...

Gracias por cualquier idea.

Respuesta

18

Al igual que con cualquier código asíncrono, las pruebas unitarias deben tener en cuenta la señalización del hilo. .NET incluye un tipo de llamada AutoResetEvent que puede bloquear el hilo de prueba hasta que se ha completado una operación asincrónica:

public class MyAsyncController : Controller 
{ 
    public void TransactionAsync() 
    { 
    AsyncManager.Parameters["result"] = "result"; 
    } 

    public ContentResult TransactionCompleted(string result) 
    { 
    return Content(result); 
    } 
} 

[TestFixture] 
public class MyAsyncControllerTests 
{ 
    #region Fields 
    private AutoResetEvent trigger; 
    private MyAsyncController controller; 
    #endregion 

    #region Tests 
    [Test] 
    public void TestTransactionAsync() 
    { 
    controller = new MyAsyncController(); 
    trigger = new AutoResetEvent(false); 

    // When the async manager has finished processing an async operation, trigger our AutoResetEvent to proceed. 
    controller.AsyncManager.Finished += (sender, ev) => trigger.Set(); 

    controller.TransactionAsync(); 
    trigger.WaitOne() 

    // Continue with asserts 
    } 
    #endregion 
} 

Espero que ayude :)

+0

No puedo esperar a probarlo en el código, pero se ve fabuloso. Lo marcará como respondido después de que lo ejecute. ¡Muchas gracias! – ChrisW

1

He escrito AsyncController método de extensión corta que simplifica la unidad de prueba de una poco.

static class AsyncControllerExtensions 
{ 
    public static void ExecuteAsync(this AsyncController asyncController, Action actionAsync, Action actionCompleted) 
    { 
     var trigger = new AutoResetEvent(false); 
     asyncController.AsyncManager.Finished += (sender, ev) => 
     { 
      actionCompleted(); 
      trigger.Set(); 
     }; 
     actionAsync(); 
     trigger.WaitOne(); 
    } 
} 

De esa manera, simplemente puede ocultar enhebrar 'ruido':

public class SampleAsyncController : AsyncController 
{ 
    public void SquareOfAsync(int number) 
    { 
     AsyncManager.OutstandingOperations.Increment(); 

     // here goes asynchronous operation 
     new Thread(() => 
     { 
      Thread.Sleep(100); 

      // do some async long operation like ... 
      // calculate square number 
      AsyncManager.Parameters["result"] = number * number; 

      // decrementing OutstandingOperations to value 0 
      // will execute Finished EventHandler on AsyncManager 
      AsyncManager.OutstandingOperations.Decrement(); 
     }).Start(); 
    } 

    public JsonResult SquareOfCompleted(int result) 
    { 
     return Json(result); 
    } 
} 

[TestFixture] 
public class SampleAsyncControllerTests 
{ 
    [Test] 
    public void When_calling_square_of_it_should_return_square_number_of_input() 
    { 
     var controller = new SampleAsyncController(); 
     var result = new JsonResult(); 
     const int number = 5; 

     controller.ExecuteAsync(() => controller.SquareOfAsync(number), 
           () => result = controller.SquareOfCompleted((int)controller.AsyncManager.Parameters["result"])); 

     Assert.AreEqual((int)(result.Data), number * number); 
    } 
} 

Si usted quiere saber más he escrito un post sobre cómo Unit test ASP.NET MVC 3 asynchronous controllers using Machine.Specifications O si lo desea comprobar esta código está en un github

Cuestiones relacionadas