Сборка юнит-тестов для MVC2 AsyncControllers - PullRequest
10 голосов
/ 03 июня 2010

Я подумываю переписать некоторые из моих контроллеров 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 ...

Спасибо за любые идеи.

Ответы [ 2 ]

18 голосов
/ 04 июня 2010

Как и в случае любого асинхронного кода, модульное тестирование должно учитывать сигнализацию потоков. .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
}

Надеюсь, это поможет:)

1 голос
/ 23 сентября 2012

Я написал короткий метод расширения 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);
    }
}

Если вы хотите узнать больше, я написал сообщение в блоге о том, как модульное тестирование асинхронных контроллеров ASP.NET MVC 3 с использованием Machine.Specifications Или, если вы хотите проверить этот код на github

...