Испытания зданий для MVC2 AsyncControllers
Я рассматриваю возможность перезаписи некоторых из моих контроллеров MVC как асинхронных контроллеров. У меня есть тестовые тесты для этих контроллеров, но я пытаюсь понять, как их поддерживать в среде асинхронного контроллера.
Например, в настоящее время у меня есть действие вроде этого:
public ContentResult Transaction()
{
do stuff...
return Content("result");
}
и мой unit test в основном выглядит следующим образом:
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...
Спасибо за любые идеи.
Ответы
Ответ 1
Как и в случае любого асинхронного кода, модульное тестирование должно быть осведомлено о сигнализации потоков..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
}
Надеюсь, что помогает:)
Ответ 2
Я написал короткий метод расширения 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 с использованием Machine.Specifications
Или, если вы хотите проверить этот код на github