为 MVC2 AsyncController 构建单元测试

发布于 2024-09-04 06:53:24 字数 700 浏览 12 评论 0原文

我正在考虑将我的一些 MVC 控制器重写为异步控制器。我对这些控制器进行了工作单元测试,但我试图了解如何在异步控制器环境中维护它们。

例如,目前我有一个这样的操作:

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

你认为应该如何构建你的单元测试?您当然可以在测试方法中调用异步启动器方法,但是如何获取返回值呢?

我在谷歌上没有看到任何关于此的信息...

感谢您的任何想法。

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 them in an async controller environment.

For example, currently I have an action like this:

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

and my unit test basically looks like:

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

Ok, that's easy enough.

But when your controller changes to look like this:

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

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

How do you suppose your unit tests should be built? You can of course invoke the async initiator method in your test method, but how do you get at the return value?

I haven't seen anything about this on Google...

Thanks for any ideas.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

ぃ双果 2024-09-11 06:53:24

与任何异步代码一样,单元测试需要了解线程信号。 .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
}

希望有帮助:)

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 :)

浅沫记忆 2024-09-11 06:53:24

我编写了简短的 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);
    }
}

如果您想了解更多信息,我已经写了一篇关于如何使用 Machine.Specifications 单元测试 ASP.NET MVC 3 异步控制器
或者,如果您想检查此代码,它位于 github

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文