在C#單元測試中使用HttpContext的簡單解決辦法

來源:互聯網
上載者:User

情境:最近在測試一個.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的單元測試。步驟如下,在測試方法前面加上以下屬性

123
[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了。

1234567891011
[[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類;下面是一段簡單的代碼

1234
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()方法:

123456789101112131415
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的時候就是這樣子:

1234567
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好啊。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.