【问题标题】:Unit test or runtime ActionResult string output response via ExecuteResult?通过 ExecuteResult 进行单元测试或运行时 ActionResult 字符串输出响应?
【发布时间】:2010-11-18 15:17:43
【问题描述】:

问题...

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

使用...

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

具体...

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

为什么?

1。在单元测试中

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

2。通过工作线程运行时

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

我看到 RuntimeUnit Tests 都使用了相同的代码,因为它会非常相似?

绊脚石1

如何正确设置模拟以不要求路由调用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 提到集成测试,但现在这个问题还有一个运行时元素。

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

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

【问题讨论】:

  • 视图引擎在执行管道中执行的字符串响应比控制器操作晚得多。你到底想在这里测试什么:你的控制器还是你的视图?因为如果它是您的控制器,那么测试生成的字符串绝对没有意义。只需测试它返回正确 ActionResult 类型的操作即可。
  • @Darin Dimtrov 感谢您的评论。我特别想获取 ActionResult 类型的文本内容。不幸的是,我似乎想同时测试控制器和视图,因为查询字符串参数可以在控制器选择的数据和视图输出中创建更改的组合。在这种情况下独立测试这些并不是真正的测试。

标签: asp.net asp.net-mvc unit-testing asp.net-mvc-2 actionresult


【解决方案1】:

您要实现的不再是单元测试,而是集成测试,因为您不再孤立地测试应用程序组件,并且有很多很棒的工具可以让您做到这一点,例如 Ultimate 中的 SeleniumWeb Tests Visual Studio 版本。

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

【讨论】:

  • 非常感谢您的回答。我已经更新了这个问题,包括通过工作线程运行,而不仅仅是通过单元测试。如果您的回答仅适用于单元测试,我们将不胜感激且有效 - 关于运行时方面的任何想法?
  • @Dean,我不太了解这个运行时方面。所以你是说你的控制器动作基本上会生成javascript,你正在尝试测试这个动作的输出?给定一些用户输入生成的确切javascript?你说的后台工作线程是什么?这是您尝试测试的应用程序的这一部分,还是其他一些应用程序调用 MVC 应用程序?最后,您能否提供一个您正在尝试测试的操作的示例?
  • 是的,这些操作会生成 JavaScript,但也会生成 XML、JSON 和纯文本,我希望在测试中测试输出。但是 MVC 应用程序有一个后台工作线程,当特定 DB 值发生更改时,它会根据工作人员引用的脏列表运行。工作人员查找这些脏项目并(通过 FTP 和 HTTP POST)将 JavaScript、JSON、XML 等推送到另一个位置。
  • selenium 和 Web 测试解决方案需要登录用户和解锁会话,对吗?如果是这样,还有什么可以解决这个问题的吗?
【解决方案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"));
    });
}

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

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

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

【讨论】:

  • 查看 MVCintegrationTestFramework 的源代码似乎是一种有趣的方法。但是对于我的项目的运行时(非测试)方面来说,为每个请求设置一个 AppInstance 等似乎很昂贵。
  • 我不太明白您所说的运行时方面是什么意思。您真正想做的是集成测试(将所有组件一起测试)。您不应在生产中使用此代码或用它处理实际请求。生成的视图,它们是带有布局等的完整网页吗?如果显示的是简单的文本,也许您可​​以在 ViewData.Model 中设置输出数据并生成 XmlResult 或 JsonResult 或您需要显示的任何内容,并让它们从 ViewData.Model 中读取?
  • 不幸的是,输出格式有多种类型,包括完整的 HTML 页面、JavaScript、JSON、XML 等。我更喜欢使用 MVC 视图系统,而不是在不应该的地方强制输出是。不过还是谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-31
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多