Модульное тестирование или время выполнения ActionResult строковый ответ через ExecuteResult? - PullRequest
5 голосов
/ 18 ноября 2010

Вопрос ...

Каков наилучший способ модульного тестирования строкового ответа и типа содержимого из нескольких методов контроллера?

Использование ...

Каждый метод возвращает ActionResult, некоторые из которых являются ViewResult ответами. Я использую ASP.NET MVC 2 RTM и Moq .

В частности ...

Я хочу получить TextWriter из HttpContext.Response, чтобы он содержал полный строковый ответ от ActionResult.

Почему?

1. В рамках модульных тестов

Я хочу проверить некоторые конкретные, если содержимое имеет , а не не существует с выводом.

2. Время выполнения через рабочий поток

Я использую фоновый рабочий поток для обновления статического контента на удаленных серверах, этот контент является выходом из контроллеров и должен быть сгенерирован как таковой. Отправлять запросы на один и тот же сервер через HTTP не рекомендуется, поскольку обновляется множество файлов тысяч.

Я вижу, что один и тот же код используется как в Runtime , так и в Unit Tests , как это будет очень похоже?

Камень преткновения 1

Как правильно настроить макет, чтобы не требовать маршрутов Или вызов RegisterRoutes и RegisterAllAreas вызов был успешным, в настоящее время выдается исключение глубоко внутри BuildManagerWrapper::IBuildManager.GetReferencedAssemblies.

Пример кода

Мои помощники-насмешники выглядят так:

public static HttpContextBase FakeHttpContext()
{
    var context = new Mock<HttpContextBase>();
    var request = new Mock<HttpRequestBase>();
    var response = new Mock<HttpResponseBase>();
    var session = new Mock<HttpSessionStateBase>();
    var server = new Mock<HttpServerUtilityBase>();
    var writer = new StringWriter();

    var form = new NameValueCollection();
    var queryString = new NameValueCollection();
    request.Setup(r => r.Form).Returns(form);
    request.Setup(r => r.QueryString).Returns(queryString);

    context.Setup(ctx => ctx.Request).Returns(request.Object);
    context.Setup(ctx => ctx.Response).Returns(response.Object);
    context.Setup(ctx => ctx.Session).Returns(session.Object);
    context.Setup(ctx => ctx.Server).Returns(server.Object);
    context.Setup(ctx => ctx.Response.Output).Returns(writer);

    return context.Object;
}

public static void SetFakeControllerContext(this Controller controller)
{
    var httpContext = FakeHttpContext();
    var routeData = new RouteData();
    var routeData = RouteTable.Routes.GetRouteData(httpContext);
    ControllerContext context = new ControllerContext(new RequestContext(httpContext, routeData), controller);
    controller.ControllerContext = context;
}

И моя текущая попытка использования TestMethod выглядит следующим образом:

[TestMethod]
public void CodedJavaScriptAction_Should_Return_JavaScript_Response()
{
    // Arrange
    var controller = new CodedController();
    controller.SetFakeControllerContext();

    // Act
    var result = controller.CodedJavaScript(); // Response is made up as a ViewResult containing JavaScript.
    var controllerContext = controller.ControllerContext;
    var routeData = controllerContext.RouteData;
    routeData.DataTokens.Add("area", "Coded");
    routeData.Values.Add("area", "Coded");
    routeData.Values.Add("controller", "Coded");
    routeData.Values.Add("action", "CodedJavaScript");

    var response = controllerContext.HttpContext.Response;
    response.Buffer = true;
    var vr = result as ViewResult;
    vr.MasterName = "CodedJavaScript";

    result.ExecuteResult(controllerContext);

    // Assert
    var s = response.Output.ToString();
    Assert.AreEqual("text/javascript", response.ContentType);
    Assert.IsTrue(s.Length > 0);
    // @todo: Further tests to be added here.   

}

Моя область, просмотры и общие файлы:

Areas\Coded\Controllers\CodeController.cs
- Areas\Coded\Views\Coded\CodedJavaScript.aspx
- Areas\Coded\CodedAreaRegistration.cs
- Views\Shared\CodedJavaScript.Master

EDIT : отредактировано и теперь включает в себя как модульное тестирование, так и выполнение во время выполнения. Спасибо @Darin Dimitrov за упоминание Integration Testing , но теперь в этом вопросе также есть элемент времени выполнения.

EDIT : после некоторого тестирования и обзора с использованием некоторого исходного кода MvcIntegrationTestFramework, на который ссылается alexn . При использовании AppDomain.CreateDomain и SimpleWorkerRequest для создания нового запроса я обнаружил, что невозможно создать новый запрос с помощью этого метода в процессе, который уже имеет активный запрос, из-за используемых значений static. Так что это исключает этот метод.

Вероятно, та же проблема, но мне сейчас интересно, можно ли результат из частичного просмотра вернуть в виде строки более напрямую?

Ответы [ 2 ]

3 голосов
/ 20 ноября 2010

То, что вы пытаетесь достичь, это больше не модульные тесты, а интеграционное тестирование, потому что вы больше не тестируете компоненты приложения изолированно и есть отличные инструменты, которые позволяют вам делать это, например Selenium или Веб-тесты в окончательных версиях Visual Studio.

Преимущество этих тестов состоит в том, что они имитируют пользовательские запросы и охватывают все поведение приложения.Таким образом, для данного пользовательского запроса вы можете утверждать, что ваше приложение отвечает правильно.Идея состоит в том, что вы пишете пользовательские сценарии, записываете их, а затем можете автоматизировать выполнение этих тестов, чтобы утверждать, что ваше приложение реагирует так, как указано.

2 голосов
/ 20 ноября 2010

Я пользуюсь Стивеном Сандерсонсом MvcIntegrationTestFramework с большим успехом.Он очень прост в использовании.

С этим вы можете легко протестировать выходной отклик, viewdata , файлы cookie, сеанс и многое другое с минимальными усилиями.

протестируйте визуализированный HTML с помощью теста, который выглядит примерно так:

[Test]
public void Output_Contains_String()
{
    appHost.SimulateBrowsingSession(session => {
        var result = session.ProcessRequest("/");
        Assert.IsTrue(result.ResponseText.Contains("string to check for"));
    });
}

Без насмешек и регистрации маршрутов.Очень чисто.

Поскольку это технически интеграционный тест, для его настройки и запуска потребуется некоторое время.

Пожалуйста, дайте мне знать, если вам нужны еще примеры или дополнительная информация.

...