1

我仍在尝试为 ASP.NET 站点(不是 MVC)添加一些单元测试。确切地说,我需要测试的方法之一是使用该HttpRequest Request对象。Request.Path我正在尝试使用 Visual Studio 2008 的内置测试框架编写测试。每当测试执行有问题的方法时,我都会收到一个System.Web.HttpExecption: Request is not Available in this context. 我明白为什么它不可用(没有正在运行的 Web 服务器,也没有提供路径),但我该如何继续测试该方法?

既然大家都喜欢看代码,下面是有问题的代码:

protected string PageName
{
    get 
    {
        return Path.GetFileName(Request.Path).Substring(0, Path.GetFileName(Request.Path).Length - 5); 
    }
}

protected Change SetupApproval(string changeDescription)
{
    Change change = Change.GetInstance();
    change.Description = changeDescription;
    change.DateOfChange = DateTime.Now;
    change.Page = PageName;
    return change;
}

这是测试:

[TestMethod]
public void SetupApproval_SubmitChange_ValidateDescription()
{
    var page = new DerivedFromInternalAppsBasePage_ForTestingOnly();
    var messageToTest = "This is a test description";
    var change = page.SetupApproval(messageToTest);
    Assert.IsTrue(messageToTest == change.Description);
}

此外,我在这里阅读了微软的文档:http: //msdn.microsoft.com/en-us/library/ms182526 (v=vs.90).aspx 并尝试使用他们建议的[HostType("ASP.NET")],[UrlToTest(http://localhost:port/pathToAspxPage.aspx")][AspNetDevelopmentServer("C:\PathToDllAssembly", "NotSureParameter")]Attributes ,但是没运气。(如您所见,我不确定应该为一些参数使用什么。)。

最后,我尝试了 Phil Haack 的 TestWebServer http://haacked.com/archive/2006/12/12/Using_WebServer.WebDev_For_Unit_Tests.aspx 并阅读了 Scott Hanselman 的帖子http://www.hanselman.com/blog/NUnitUnitTestingOfASPNETPagesBaseClassesControlsAndOtherWidgetryUsingCassiniASPNETWebMatrixVisualStudioWebDeveloper.aspx For Phil 的服务器,我不确定我会在ExtractResource方法中使用什么参数。

4

4 回答 4

3

我最近一直在努力解决类似的问题,真正有用的是来自 Microsoft Research 的 Moles 框架 - http://research.microsoft.com/en-us/projects/moles/。它允许您从 BCL 伪造任何东西,包括 HttpContext.Current。

于 2012-05-17T14:24:35.213 回答
2

有一个与您遇到的问题非常相似的问题,该问题在Michael Feathers的“有效使用遗留代码”一书中进行了描述。特别地,重构被称为“适应参数”。

您的代码的“问题”是它直接耦合到 HttpRequest,特别是 Request.Path。因此,总体方法是您希望将代码与 HttpRequest 分离。

与上面的建议类似,这里是按照 Michael Feather 书中的想法进行解耦的另一种方法。我没有尝试编译这个,所以请原谅任何拼写错误或语法错误。

公共接口 ParameterSource
{
    公共字符串路径 {get; }
}

公共类 FakeParameterSource : ParameterSource
{
   公共字符串值;
   公共字符串路径 { 获取 { 返回值 } }
}

公共类 RealParameterSource : ParameterSource
{
   私有 HttpRequest 请求;
   公共RealParameterSource(HttpRequest aRequest)
   {
     请求 = 一个请求;
   }
   公共字符串路径 { 获取 { 返回请求。路径 } }
}

现在这是您需要更改的重要部分(这是一种方法):

// 小改名
受保护的字符串 GetPageName(ParameterSource source)
{
   返回 Path.GetFileName(source.Path).Substring(0, Path.GetFileName(source.Path).Length - 5);
}

以上注入发生在方法级别。您也可以通过构造函数或属性来完成。

您的测试代码现在可能类似于:

protected Change SetupApproval(string changeDescription)
{
    ParameterSource p = new FakeParameterSource { Value = "mypath" };
    更改更改 = Change.GetInstance();
    更改.描述 = 更改描述;
    change.DateOfChange = DateTime.Now;
    change.Page = GetPageName(p); // 现在使用参数源中的值
    退换货;
}

我希望你明白这个想法并发现这很有用

于 2012-05-22T18:47:53.870 回答
0

测试使用 HttpContext 的方法的关键是使用 HttpContextBase。HttpContextBase 是一个抽象类,使其易于模拟。不幸的是 HttpContext 没有实现 HttpContextBase 所以你需要使用 HttpContextWrapper 将真正的 HttpContext 包装到 HttpContextBase 中。这篇文章有一个很好和简单的解释:http ://splinter.com.au/httpcontext-vs-httpcontextbase-vs-httpcontext

您的问题没有说明示例代码的位置。如果这一切都在代码隐藏中,那么您将很难尝试对其进行测试。您应该查看Model-View-Presenter(MVP) 模式,而不是在代码隐藏中使用逻辑。MVP 有一些框架,但自己做也很容易。

在 MVP 中,presenters 包含所有逻辑和页面,代码隐藏仅实现 View-interface 包含可以绑定到 UI 的属性。所有依赖项都可以注入到 Presenter 中,使其易于测试。

于 2012-05-21T04:47:40.370 回答
0

我同意@Alex Taylor(抱歉,我无法评论):对(非 MVC)Web 应用程序进行单元测试的最佳方法是确保这些位依赖于存在的整个环境;毕竟,这就是它被称为单元测试的原因。所以你可以PageName用这个替换:

protected string PageName
{
    get 
    {
        return GetPageName(Request.Path);
    }
}

protected static string GetPageName(string path)
{
    return Path.GetFileNameWithoutExtension(path);
}

有了这个,您可以进行单元测试GetPageName,并确信 PageName 可以按预期工作。

请记住,尝试进行单元测试是没有意义的Request.Path,因为它具有应该满足的预期行为(或“合同”)。如果您不确定 Request.Path 在不同情况下会返回什么,您应该查看文档而不是添加单元测试。

于 2012-05-22T09:22:36.490 回答