Building unit tests for MVC2 AsyncControllers

后端 未结 2 822
无人及你
无人及你 2021-02-20 16:24

I\'m considering re-rewriting some of my MVC controllers to be async controllers. I have working unit tests for these controllers, but I\'m trying to understand how to maintain

相关标签:
2条回答
  • 2021-02-20 16:47

    As with any async code, unit testing needs to be aware of thread signalling. .NET includes a type called AutoResetEvent which can block the test thread until an async operation has been completed:

    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
    }
    

    Hope that helps :)

    0 讨论(0)
  • 2021-02-20 16:56

    I've written short AsyncController extension method that simplifies unit testing 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();
        }
    }
    

    That way we can simply hide threading 'noise':

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

    If you want to know more I've written a blog post about how to Unit test ASP.NET MVC 3 asynchronous controllers using Machine.Specifications Or if you want to check this code it's on a github

    0 讨论(0)
提交回复
热议问题