2010-06-03 45 views
10

我正在考慮重新改寫我的一些MVC控制器是異步控制器。我有這些控制器的工作單元測試,但我想了解如何在異步控制器環境中維護它們。建設單元測試MVC2 AsyncControllers

例如,目前我有一個這樣的動作:

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

和我的單元測試基本上是這樣的:

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

好吧,這是很容易。

但是當你的控制器的變化,看起來像這樣:

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

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

你怎麼想你的單元測試應該建?您當然可以在您的測試方法中調用異步啓動器方法,但是如何獲取返回值?

我還沒有看到這個在谷歌什麼...

感謝您的任何想法。

回答

18

與任何異步代碼一樣,單元測試需要知道線程信號。 .NET包括稱爲的AutoResetEvent類型能阻斷測試線程,直到一個異步操作已經完成:

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 
} 

希望幫助:)

+0

我迫不及待想在代碼中嘗試,但它看起來很棒。將它標記爲在我運行後回答。非常感謝! – ChrisW 2010-06-04 21:36:33

1

我已經寫了簡化單元測試短AsyncController擴展方法位。

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

這樣,我們可以簡單地隱藏線程的「噪音」:

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

如果你想知道更多,我寫了一個博客張貼有關如何Unit test ASP.NET MVC 3 asynchronous controllers using Machine.Specifications 或者,如果你要檢查這個代碼它是在github