2010-06-03 11 views
10

Ich überlege mir, einige meiner MVC-Controller als asynchrone Controller neu zu schreiben. Ich habe Working-Unit-Tests für diese Controller, aber ich versuche zu verstehen, wie man sie in einer asynchronen Controller-Umgebung verwaltet.Baueinheitstests für MVC2 AsyncControllers

Zum Beispiel zur Zeit habe ich eine Aktion wie folgt aus:

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

und meine Unit-Test sieht im Grunde wie:

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

Ok, das ist einfach genug.

Aber wenn Ihre Controller Änderungen wie folgt aussehen:

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

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

Wie schätzen Sie Ihre Unit-Tests annehmen, gebaut werden sollte? Sie können natürlich die asynchrone Initiatormethode in Ihrer Testmethode aufrufen, aber wie erhalten Sie den Rückgabewert?

Ich habe nichts darüber auf Google gesehen ...

Vielen Dank für alle Ideen.

Antwort

18

Wie bei jedem Async-Code muss der Komponententest auf die Thread-Signalisierung achten. .NET enthält eine Art Autoreset genannt, die den Test-Thread blockieren können, bis ein Async-Betrieb abgeschlossen ist:

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 
} 

Hoffnung, das hilft :)

+0

Ich kann es kaum erwarten, das im Code auszuprobieren, aber es sieht fabelhaft aus. Will es als beantwortet markieren, nachdem ich es ausgeführt habe. Vielen Dank! – ChrisW

1

I kurze AsyncController Erweiterungsmethode geschrieben habe, das Gerät zu testen vereinfacht a Bit.

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

So können wir einfach ‚Rauschen‘ verstecken Threading kann:

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

Wenn Sie wissen möchten, mehr ich über einen Blog-Post geschrieben habe, wie man Unit test ASP.NET MVC 3 asynchronous controllers using Machine.Specifications Oder wenn Sie möchten, um dies überprüfen Code ist ein github