2014-03-06 20 views
51

我有一項服務使用Microsoft.Net.Http來檢索一些Json數據。大!如何在.NET測試中傳遞模擬的HttpClient?

當然,我不希望我的單元測試碰到實際的服務器(否則,這是一個集成測試)。

這裏是我的服務構造函數(它使用依賴注入...)

public Foo(string name, HttpClient httpClient = null) 
{ 
... 
} 

我不知道我怎麼可以...說.. MoqFakeItEasy嘲笑這一點。

我想確保當我的服務電話GetAsyncPostAsync ..然後我可以僞造這些電話。

任何建議我怎麼能做到這一點?

我-hoping-我並不需要使自己的包裝..因爲那的廢話:(微軟無法做出這種監督,對不對?

(是的,它很容易做包裝..我做過他們之前......但它的重點!)

+1

_「我是 - 希望 - 我不需要製作我自己的包裝器」_ - 您使用了多少個「HttpClient」的方法和屬性?通常爲這些創建一個接口是有用的,然後你可以模擬它。 – CodeCaster

+0

(就像我在OP中提到的那樣)同意 - 這很容易和簡單......但我會盡管我不應該?這是應該核心的東西..對嗎?這是我唯一的選擇嗎? –

+1

據我所知,如果他們是虛擬的,你可以將這些呼叫存根。因爲他們不是,我會假設你需要編寫一個包裝(這是更簡潔的方式)。 – ElGauchooo

回答

84

你可以用一個假的替換核心HttpMessageHandler。看起來像這樣的東西...

public class FakeResponseHandler : DelegatingHandler 
    { 
     private readonly Dictionary<Uri, HttpResponseMessage> _FakeResponses = new Dictionary<Uri, HttpResponseMessage>(); 

     public void AddFakeResponse(Uri uri, HttpResponseMessage responseMessage) 
     { 
       _FakeResponses.Add(uri,responseMessage); 
     } 

     protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) 
     { 
      if (_FakeResponses.ContainsKey(request.RequestUri)) 
      { 
       return _FakeResponses[request.RequestUri]; 
      } 
      else 
      { 
       return new HttpResponseMessage(HttpStatusCode.NotFound) { RequestMessage = request}; 
      } 

     } 
    } 

然後你可以創建一個客戶端,將使用假處理程序。

var fakeResponseHandler = new FakeResponseHandler(); 
fakeResponseHandler.AddFakeResponse(new Uri("http://example.org/test"), new HttpResponseMessage(HttpStatusCode.OK)); 

var httpClient = new HttpClient(fakeResponseHandler); 

var response1 = await httpClient.GetAsync("http://example.org/notthere"); 
var response2 = await httpClient.GetAsync("http://example.org/test"); 

Assert.Equal(response1.StatusCode,HttpStatusCode.NotFound); 
Assert.Equal(response2.StatusCode, HttpStatusCode.OK); 
+0

很好的解決一個有趣的問題。從字面上看,除了通過網絡發送HTTP之外,其他所有內容都可以做到 – Spence

+0

您的示例將不會按原樣編譯。你需要使用Task.FromResult來返回你的HttpResponseMessage實例。 – Ananke

+1

@Ananke SendAsync方法上的async關鍵字對你來說是神奇的。 –

1

你可以看看Microsoft Fakes,尤其是在Shims部分。有了它們,你可以修改行爲你的HttpClient本身,前提是你正在使用VS Premium或Ultimate。

+3

我無法訪問這兩個昂貴的版本。 –

2

我只想做一個小的變化,以@Darrel Miller的答案,這是使用Task.FromResult避免約異步方法期待的AWAIT操作者的警告。

public class FakeResponseHandler : DelegatingHandler 
{ 
    private readonly Dictionary<Uri, HttpResponseMessage> _FakeResponses = new Dictionary<Uri, HttpResponseMessage>(); 

    public void AddFakeResponse(Uri uri, HttpResponseMessage responseMessage) 
    { 
     _FakeResponses.Add(uri, responseMessage); 
    } 

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) 
    { 
     if (_FakeResponses.ContainsKey(request.RequestUri)) 
     { 
      return Task.FromResult(_FakeResponses[request.RequestUri]); 
     } 
     else 
     { 
      return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound) { RequestMessage = request }); 
     } 
    } 
} 
+3

我會謹慎使用該技術在性能的任何環境因爲它可能會導致線程切換無益。如果您真的關注警告,那麼只需刪除async關鍵字並使用Task.FromResult。 –

+4

如果實現不是基於任務的,最好返回Task.FromResult並移除async關鍵字而不是使用Task.Run。返回Task.FromResult(response); – user3285954

17

我知道這是一個老問題,但我對這個專題的搜索過程中,它迷迷糊糊地發現了一個非常好的解決方案,使測試更容易HttpClient

它通過的NuGet可:

https://github.com/richardszalay/mockhttp

PM> Install-Package RichardSzalay.MockHttp 

這裏是一個快速瀏覽一下在用法:GitHub的項目頁面上

var mockHttp = new MockHttpMessageHandler(); 

// Setup a respond for the user api (including a wildcard in the URL) 
mockHttp.When("http://localost/api/user/*") 
     .Respond("application/json", "{'name' : 'Test McGee'}"); // Respond with JSON 

// Inject the handler or client into your application code 
var client = new HttpClient(mockHttp); 

var response = await client.GetAsync("http://localost/api/user/1234"); 
// or without await: var response = client.GetAsync("http://localost/api/user/1234").Result; 

var json = await response.Content.ReadAsStringAsync(); 

// No network connection required 
Console.Write(json); // {'name' : 'Test McGee'} 

更多信息。 希望這可以是有用的。

相關問題