5

问题...

从多个 Controller 方法对字符串响应内容类型进行单元测试的最佳方法是什么?

使用...

每个方法都返回一个ActionResult,其中一些是ViewResult响应。我正在使用 ASP.NET MVC 2 RTM 和Moq

具体来说...

我希望获得TextWriterfromHttpContext.Response并让它包含来自ActionResult.

为什么?

1. 在单元测试中

我想测试一些特定的内容是否与输出一起存在和存在。

2. 通过工作线程运行时

我使用后台工作线程来更新远程服务器上的静态内容,该内容是控制器的输出,必须这样生成。不建议通过 HTTP 向同一服务器发出请求,因为有许多 1000 个文件需要更新。

我看到在Runtime和通过Unit Tests都使用了相同的代码,因为它会非常相似吗?

绊脚石1

如何正确设置模拟以不需要 Routes调用RegisterRoutesRegisterAllAreas让调用成功,目前在深处引发异常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

编辑:编辑现在包括单元测试和运行时执行。感谢@Darin Dimitrov 提到集成测试,但现在这个问题还有一个运行时元素。

编辑:在使用alexn 引用的 MvcIntegrationTestFramework中的一些源代码进行一些测试和审查之后 。使用AppDomain.CreateDomainSimpleWorkerRequest创建新请求,我发现由于使用的static值,在已经有活动请求的进程中无法通过此方法创建新请求。所以这排除了这种方法。

可能是同样的问题,但我现在想知道部分视图的结果是否可以更直接地作为字符串返回?

4

2 回答 2

3

您尝试实现的不再是单元测试,而是集成测试,因为您不再孤立地测试应用程序组件,并且有很棒的工具可以让您执行此操作,例如Visual Studio 终极版本中的SeleniumWeb 测试

这些测试的优点是它们模拟用户请求并涵盖整个应用程序行为。因此,对于给定的用户请求,您可以断言您的应用程序正确响应。这个想法是您编写用户场景,记录它们,然后您可以自动执行这些测试以断言您的应用程序按照指定的方式响应。

于 2010-11-20T16:51:58.523 回答
2

我使用 Steven Sandersons 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"));
    });
}

没有模拟和路线注册。很干净。

由于这在技术上是一个集成测试,因此需要一些时间来设置和运行。

如果您需要更多示例或更多信息,请告诉我。

于 2010-11-20T17:18:22.790 回答