2016-06-21 68 views
1

我已被分配爲我沒有修改權限的應用程序編寫單元測試。我想單元測試的方法進行調用是這樣的:如何爲單元測試填充HttpWebResponse.GetResponseAsync()?

HttpWebResponse response = await request.GetResponseAsync() as HttpWebResponse; 

我已經使用微軟正版正貨千在這個項目中的其他測試,所以我想我會做同樣的在這裏。在我看來,最簡單,最乾淨的解決方案就是使用request.GetResponseAsync()方法。然後,我可以返回一些假內容,並確保該方法正確處理它,而無需實際發出請求。

GetResponseAsync()返回一個Task<WebResponse>對象。所以我通常會做這樣的事情:

using (ShimsContext.Create()) 
{ 
    System.Net.Fakes.ShimWebRequest.AllInstances.GetResponseAsync = (x) => 
    { 
     return new Task<WebResponse>(() => 
     { 
      HttpWebResponse toRet = new HttpWebResponse(); 
      return toRet; 
     }); 
    } 
} 

的問題是,上面並沒有編譯,因爲

「System.Net.HttpWebResponse.HttpWebResponse()」已過時:「這種API 支持.NET Framework基礎結構,並不打算直接從您的代碼中使用 '。

我知道這個類型已經過時了,現在我們應該使用一些不同的東西,但是我試圖測試的遺留代碼並不允許我那麼奢侈。我看了很多關於這個話題的問題,但似乎沒有人回答這個問題。

回答

7

我會將我的答案分成兩部分;第一部分是您正在尋找的解決方案......第二部分我將討論您在UT環境中的其他選項(因此,此答案將有助於其他人...)

既然你已經使用MsFakes,您可以使用Shims創建一個實例。 以下片段是其示出了一個實例的方式來初始化和使用ShimHttpWebResponse

[Test] 
public async Task InitializeShimHttpWebResponse() 
{ 
    using (ShimsContext.Create()) 
    { 
     ShimWebRequest.AllInstances.GetResponseAsync = (x) => 
     { 
/* you can replace the var with WebResponse if you aren't going to set any behavior */ 
      var res = new ShimHttpWebResponse(); 
      return Task.FromResult((WebResponse)res); 
     }; 

     ShimWebRequest.CreateString = uri => 
     { 
      WebRequest req = new ShimFileWebRequest(); 
      return req; 
     }; 

     var request = WebRequest.Create(""); 
     var response = await request.GetResponseAsync() as HttpWebResponse; 

     Assert.IsNotNull(response); 
    } 
} 

僞造品配置:

<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/"> 
    <Assembly Name="System" Version="4.0.0.0"/> 
    <ShimGeneration> 
    <Add FullName="System.Net.HttpWebResponse"/> 
    <Add FullName="System.Net.WebRequest"/> 
    <Add FullName="System.Net.HttpWebRequest"/> 
    <Add FullName="System.Net.FileWebRequest"/>  
    </ShimGeneration> 
</Fakes> 

只是爲了總結本節;國際海事組織,對於一般情況下,使用代碼編織工具(MsFakes)是正確的方式來處理這種情況(我在下一節中更詳細地解釋)

正如我看到你有4個選項來創建一個新的實例的HttpWebResponse

使用反射 - 在這種情況下,一個壞主意(UT ..)

2.傳承 - 自定義的模擬...

使用代理b有框架Eg; Moq,Rhinomocks等

4.使用代碼編織工具(就像您已經使用過的那樣)。 Msfakes,Typemock Isolator等。

還有一個選擇:創建一個集成測試,而不是UT ...

1.反思:

HttpWebResponse有3個C'tors;(publicinternalprotected

要使用內部\公共C'tors,您將不得不使用反射,但是在大多數情況下,它不會開箱即用,然後您將不得不違反一些UT規則(小型,快速等) ...)

2例,其中反射會開箱的是:

  1. 你不調用實例任何有問題的屬性/方法和SUT(被測的人)通過這個實例來一個他的依賴關係

  2. 您將使用更多反射初始化實例字段。

雖然第一個是一個簡單的例子(如果這是你的情況,那麼你應該使用反射)第二個是UT的背景下不好的做法;你的UTs不會很小/可讀/可維護,執行時間會增加,微軟可能會做一些重構,這可能會破壞你的UTs。

對於受保護的應該通過繼承來調用它(compile vs. runtime ...)。

2.繼承:

的保護C'tor也有過時的屬性,但屬性IsError設置爲false,這可以讓你繼承這個類,然後你就可以改變方法/屬性的行爲;虛擬覆蓋,非虛擬 - 僅當您訪問類成員(或反射)

此選項的主要缺點是您必須生成的代碼數量及其複雜性。

3.使用基於代理框架:

現場這些工具使用反射來創建基於類的繼承的實例背後(合併選項1和2) 這些工具有一些內置的方法,使你的假代碼更小/可讀/可維護。

缺點:(我不會指出的基於代理的工具,所有的缺點...)

  1. 你仍然無法改變的非虛擬方法的行爲。

  2. 您沒有訪問實例成員。

這2個可以通過使用代碼編織工具來解決。

4。代碼編織工具:

這些工具可以讓你做幾乎任何事情,這就是爲什麼這些工具對於一般情況最好的原因(我可以總結句子的主要缺點 - 強大的功能帶來巨大的責任.. )。 就你而言,它們爲您提供最佳解決方案; 既然HttpWebResponse有一個非虛擬方法,你不想重構你的代碼,這對你來說是正確的解決方案。

但是Msfakes並沒有爲UT提供額外的方法/功能(AssertsWasCalled,counting等等),所以除非你打算用其他工具替換這個工具,你應該把它和代理基礎工具(免費工具!!!!)

1

您的問題與Shim無關。使用反射來繞過過時的問題。

HttpWebResponse toRet = Activator.CreateInstance<HttpWebResponse>();