10

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

単体テストをどのように構築する必要があると思いますか? もちろん、テスト メソッドで非同期イニシエーター メソッドを呼び出すこともできますが、戻り値を取得するにはどうすればよいでしょうか。

私はこれについてGoogleで何も見たことがありません...

アイデアをありがとう。

4

2 に答える 2

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
}

それが役立つことを願っています:)

于 2010-06-04T10:27:43.673 に答える
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);
    }
}

詳細を知りたい場合は、 Machine.Specifications を使用して ASP.NET MVC 3 非同期コントローラーを単体テストする方法についてのブログ記事を書きました。 または、このコードを確認したい場合は、githubにあります。

于 2012-09-23T11:17:53.103 に答える