• 在C#单元测试中使用HttpContext的简单解决办法


    场景:最近在测试一个.NET的Http Module,这个Module是用来做URL重写的。刚开始进展的比较顺利,因为该Module里面的方法参数基本上都是String,后来这个Module进行了一下重构,所有参数都变成了HttpContext了,这就直接导致原来的单元测试都跑不起来了,接着就开始了弄HttpContext了。

    1. 采用Visual Studio自带的ASP.NET单元测试

    刚开始我看了一下被测试的代码,虽然说用到了HttpContext,但是有很多地方我都可以绕过去的,意思就是这个HttpContext只是名以上需要的一个参数,只要它不是NULL就可以了,并不影响我的测试,所以我采用了ASP.NET Unit Test的办法来获取一个HttpContext,这个方法实现起来是最简单的,但是会有一些问题,后面会提及到。

    首先新建一个WEB项目,然后把被测的Http Module挂到这个新建的WEB项目中。然后就可以配置ASP.NET单元测试,把原来一般的单元测试改造成为一个ASP.NET的单元测试。步骤如下,在测试方法前面加上以下属性

    1
    2
    3
    
    [HostType("ASP.NET")]
    [UrlToTest("http://localhost:6988/Default.aspx")]
    [AspNetDevelopmentServerHost("D:\\MS_Code\\Public\\UrlRouter.WebTest", "/")]

    [UrlToTest] — 这个属性指定了运行该单元测试时的URL

    [HostType] — 一般的单元测试是在VSTest的宿主进程下运行,所以是没有HttpContext的;如果作为ASP.NET单元测试,那么必须要在ASP.NET宿主进程下运行

    [AspNetDevelopmentServerHost] — 由于我使用了ASP.NET Development Server 作为测试的主机服务器,而不是IIS;所以我需要设置这个属性来指定Web应用程序的完整路径。这里我需要把目录指向刚才新建的WEB项目的路径中。这样子就可以在单元测试中直接使用HttpContext.Current来获取一个HttpContext了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    [[TestMethod]
    [HostType("ASP.NET")]
    [AspNetDevelopmentServerHost("D:\\MS_Code\\Public\\UrlRouter.WebTest", "/")]
    [UrlToTest("http://localhost:6988/Default.aspx")]
    public void Fuseaction_PUT()
    {
        handler = new UrlMapHandler(HttpContext.Current);
        Assert.IsNotNull(handler);
        result = handler.ExecuteUrlMap();
        StringAssert.Contains(result.Url, "expected string");
    }

    这样子初步解决了需要HttpContext的困难,但是会有以下问题:

    1. 对HttpContext的内容不可控
    2. 需要运行ASP.NET Development Server
    3. 不能进行调试

    2. 直接创建HttpContext实例

    基于刚才提到的3个不足,我决定直接创建一个HttpContext来解决问题。还好,HttpContext有公开的构造函数,这个构造函数需要接受一个HttpWorkerRequest作为参数,HttpWorkerRequest是一个抽象类,就是不能直接对之实例化啦,不过好消息是,微软有一个简单的类“SimpleWorkerRequest”,这个类实现了HttpWorkerRequest类;下面是一段简单的代码

    1
    2
    3
    4
    
    TextWriter tw = new StringWriter();
    HttpWorkerRequest wr = new SimpleWorkerRequest
    ("default.aspx", "friendId=1300000000", tw);
    HttpContext.Current = new HttpContext(wr);

    这样子就能在单元测试里面用到HttpContext了,好处有:

    1. 可以控制HttpContext的内容了,对于URL重写这部分来说,会比较关心请求路径,还有queryString,这两个可以在实例化SimpleWorkerRequest的时候,作为参数传递进去。
    2. 不需要ASP.NET Development Server。这个很重要,因为可以满足一个自包含(self-contained)的单元测试的要求
    3. 可以调试

    3.改良后的方案

    到此,世界很美好,测试进行的很顺利。好日子没过多久,我又遇到一个问题,这个URL重写模块,还应用了一个规则引擎,其中进行了大量对HOST判断的操作,很不幸,用刚才的方法创建的HttpContext,它返回的Request.Url中,HOST永远都是127.0.0.1,很郁闷。这时候,一个所有做.NET的人都需要的屠龙刀派上用场了!.NET Reflector,用Reflector打开System.Web.dll,找到System.Web.Hosting这个命名空间,然后找到SimpleWorkerRequest这个类,发现里面的GetLocalAddress()方法,发现里面就是Hard-Code了一个地址:127.0.0.1;好了,知道问题在哪里了,着手写一个新的类,这个类继承SimpleWorkerRequest,然后重写他的GetLocalAddress()方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    public class MyWorkerRequest : SimpleWorkerRequest
    { 
        private string localAdd = string.Empty;
     
        public MyWorkerRequest(string page, string query, TextWriter output, string address)
            : base(page, query, output)
        {  
            this.localAdd = address;
        }
     
        public override string GetLocalAddress()
        {
            return this.localAdd;
        }
    }

    这样子,实例化HttpContext的时候就是这样子:

    1
    2
    3
    4
    5
    6
    7
    
    Thread.GetDomain().SetData(".appPath", "c:\\inetpub\\wwwroot\\webapp\\");
    Thread.GetDomain().SetData(".appVPath", "/");
    TextWriter tw = new StringWriter();
    String address = "home.myspace.cn";
    HttpWorkerRequest wr = new MyWorkerRequest
    ("default.aspx", "friendId=1300000000", tw, address);
    HttpContext.Current = new HttpContext(wr);

    需要注意的是,对于.appPath和.appVPath的设置是必须的,因为在SimpleWorkerRequest的构造函数中,会取这两个数值。
    经过这样的改造,基本上已经满足了测试的需求了。

    总结一下:

    1. 这个事情居然用了1天多时间,后来发现解决的办法就在我订阅的博客里面就有,以后遇到什么难题先找一个Google Reader。
    2. 多用.NET Reflector,Visual Studio经常让你看meta data,那些meta data对我们来说一点用处都没有,还是Reflector好啊。
  • 相关阅读:
    《世界是数字的》
    IT小小鸟读书笔记
    Codeforces Round #665 Div.2 (CF1401)
    Codeforces Round #662 Div.2 (CF1392)
    std::array的效率问题
    CSS布局学习总结
    TCP中三次握手与四次挥手
    初见Vuex
    初见webpack
    CentOS7使用yum简便安装mysql5.7
  • 原文地址:https://www.cnblogs.com/hyl8218/p/2063289.html
Copyright © 2020-2023  润新知