单元测试或运行时 ActionResult 字符串通过 ExecuteResult 输出响应?

发布于 2024-10-03 04:01:55 字数 4127 浏览 2 评论 0 原文

问题...

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

使用...

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

具体来说...

我希望从 HttpContext.Response 获取 TextWriter 并让它包含来自 ActionResult 的完整字符串响应。

为什么?

1. 在单元测试中,

我想测试输出中内容存在不存在的一些具体内容。

2. 通过工作线程运行时

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

我看到在运行时单元测试中使用了相同的代码,因为它非常相似?

绊脚石 1

如何正确设置模拟以不需要路由 Or 调用 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 创建新请求,我发现在已经有一个进程的情况下不可能通过此方法创建新请求主动请求,由于使用了静态值。所以这就排除了这种方法。

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

The Question...

What is the best way to Unit Test the string response and content type from several Controller methods?

Using...

Each method returns an ActionResult, some of which are ViewResult responses. I'm using ASP.NET MVC 2 RTM and Moq.

Specifically...

I wish to obtain the TextWriter from HttpContext.Response and have it contain the full string response from an ActionResult.

Why?

1. Within Unit Tests

I want to test some specific if content does and does not exist with the output.

2. Runtime via worker thread

I use a background worker thread to update static content on remote servers, this content is the output from the Controllers and must be generated as such. Making requests to the same server via HTTP is not advisable because there are many 1000's of files which are updated.

I see the same code being used at both Runtime and via Unit Tests, as it would be very similar?

Stumbling block 1

How to correctly setup mocking to not require Routes Or call RegisterRoutes and RegisterAllAreas have the call succeed, currently throws an exception deep inside BuildManagerWrapper::IBuildManager.GetReferencedAssemblies.

Sample Code

My mocking helpers look like this:

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

And my current attempt at a TestMethod is as follows:

[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.   

}

My area, views and shared files are:

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

EDIT: edited to now include both Unit Testing and Runtime execution. Thanks to @Darin Dimitrov for mentioning Integration Testing but there is now also a runtime element to this question.

EDIT: After some testing and review using some of the source code from MvcIntegrationTestFramework as referenced by alexn. Which uses AppDomain.CreateDomain and SimpleWorkerRequest to create a new request, I have found that it is not possible to create a new request via this method in a process that already has an active request, due to static values used. So this rules this method out.

Probably the same issue but I am now wondering if the result from a Partial View can be returned as a string more directly?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

谁与争疯 2024-10-10 04:01:55

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

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

What you are trying to achieve is no longer unit tests but integration testing because you are no longer testing your application components in isolation and there are great tools that allow you to do this like Selenium or Web Tests in the Ultimate versions of Visual Studio.

The advantage of these tests is that they simulate user requests and cover the entire application behavior. So for a given user request you can assert that your application responds properly. The idea is that you write user scenarios, record them and then you could automate the execution of those tests to assert that your application responds as specified.

独自唱情﹋歌 2024-10-10 04:01:55

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

无模拟和路由注册。非常干净。

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

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

I use Steven Sandersons MvcIntegrationTestFramework with great success. It's very easy to use.

With this, you can easily test output response, viewdata, cookies, session and a lot more with very little effort.

You would test the rendered HTML with a test looking something like this:

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

No mocking and routes registering. Very clean.

As this is technically an integration test, it will take some time to set up and run.

Please let me know if you need any more examples or some more information.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文