2012-05-21 20 views
25

プロジェクトで新しいWeb APIビットを使用していて、要求にクライアント証明書を追加する必要があるため、通常のHttpMessageRequestを使用できないことがわかりました。その結果、HttpClientを使用しています(WebRequestHandlerを使用することができます)。これはすべてうまくいきますが、少なくともRhino Mockの場合はスタブ/モックフレンドリーではありません。ASP.NET Web API HttpClientをスタブまたはモック

私は通常、代わりに使用するHttpClientの周りにラッパーサービスを作成しますが、可能ならばこれを避けたいと思います。ラップする必要があるメソッドがたくさんあるためです。私は何かが欠けていることを望んでいます—スタブの方法に関する提案HttpClient

答えて

11

私はMoqを使用し、私はHttpClientをスタブアウトすることができます。私はこれをRhino Mock(私は自分で試したことはありません)と同じだと思います。 あなただけのHttpClientをスタブにしたい場合は、以下のコードは動作するはずです:

var stubHttpClient = new Mock<HttpClient>(); 
ValuesController controller = new ValuesController(stubHttpClient.Object); 

私が間違っているなら、私を修正してください。私はここであなたがHttpClient内のメンバーをスタブすることを言及していると思います。

一番人気の分離/モックオブジェクトのフレームワークを使用すると、以下のコードは

stubHttpClient.Setup(x => x.BaseAddress).Returns(new Uri("some_uri"); 

はまた、あなたは避けたいと述べた例外をスロー例えば非仮想メンバー 上/設定をスタブすることはできません多くのHttpClientメンバーをラップするため、ラッパーを作成します。なぜたくさんのメソッドをラップする必要があるのか​​はっきりしませんが、必要なメソッドだけを簡単にラップすることができます。例えば

:私は(私はコードを書くませんので、そこにたくさんの例を)あなたのための恩恵を受けるかもしれないと思う

public interface IHttpClientWrapper { Uri BaseAddress { get; }  } 

public class HttpClientWrapper : IHttpClientWrapper 
{ 
    readonly HttpClient client; 

    public HttpClientWrapper() { 
     client = new HttpClient(); 
    } 

    public Uri BaseAddress { 
     get 
     { 
      return client.BaseAddress; 
     } 
    } 
} 

は、他のオプション マイクロソフトモグラフレームワーク http://research.microsoft.com/en-us/projects/moles/ マイクロソフトの偽物:(あなたはVS2012 Ultimateを使用している場合) http://msdn.microsoft.com/en-us/library/hh549175.aspx

+1

回答ありがとうございます。私はラッパーのアプローチで終わった。私がもう一度やらなければならないのであれば、おそらくTypeMockやMSソリューションの1つのようなものを使うでしょう。 –

+0

私は当初、密閉クラス内の特定のものの具体的な実装を書いていましたが、内側にしっかりと結合されました。しかし、私はあなたが言及したように 'Moq'でうまく動作するテストを実現するために今すぐ実現します。カバレッジ。私はこれをしなければならないことを知っていました。私はただそれを探して、怠け者ではないことを自分自身に納得させなければなりませんでした:D – ppumkin

59

すでに@Rajによって提示された優れたアイデアに代わるものとして、下のステップに行くために、および/偽を模擬することが可能です代わりに。

HttpClientを必要とするクラスをコンストラクタ内の依存性注入パラメータとして受け入れる場合は、ユニットテスト時に自分でHttpMessageHandlerを注入したHttpClientを渡すことができます。以下のように、この単純なクラスでは、あなたが実装する必要がある唯一の1つの抽象メソッドを持っています

public class FakeHttpMessageHandler : HttpMessageHandler 
    { 
    public HttpRequestMessage RequestMessage { get; private set; } 

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
     { 
     RequestMessage = request; 
     return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)); 
     } 
    } 

私の簡単な例だけでは、後で検査のために公共の財産でHttpRequestMessageを保存し、HTTP 200(OK)を返しますが、返される結果を設定するコンストラクタを追加することでこれを補うことができます。あなたはこのように、このクラスを使用すると思い

:このアプローチには限界が

public void foo() 
    { 
    //Arrange 
    var fakeHandler = new FakeHttpMessageHandler(); 
    var client = new HttpClient(fakeHandler); 
    var SUT = new ClassUnderTest(client); 

    //Act 
    SUT.DomSomething(); 

    //Assert 
    fakeHandler.RequestMessage.Method.ShouldEqual(HttpMethod.Get); // etc... 
    } 

あり、複数の要求を行うか、複数のHttpClient Sを作成する必要がある方法で、たとえば、その後、偽のハンドラが開始される可能性がありますあまりにも複雑になる。しかし、単純な場合には考慮する価値があるかもしれません。

+1

これはきちんとした考えですが、このアプローチは非常に脆いテストを作成します。 HttpClientの実装は将来変更される可能性があります。テストはクラスの内部動作に依存します。ラッパーIMHOは依然として最良のアプローチです。 –

+6

これはパブリックコンストラクタパラメータです。私はそれが変わるとは思わない。私はこの答えが大好きです。 – Kugel

+1

ファンタスティック、回答を受け入れる必要があります。私は@Kugelに同意します - それは変わる可能性がなく、私が喜んで取るリスクです。 –

34

私は便利かもしれないMockHttpと呼ばれる数ヶ月前にライブラリをリリースしました。それは流暢な(そして拡張可能な)APIを持つカスタムHttpMessageHandlerを使用します。擬似ハンドラ(またはHttpClient)をサービスクラスに挿入すると、設定されたときに応答します。

以下に、基本的な使用法を示します。 WhenRespondのメソッドには、カスタムロジックを実行することを含め、多数のオーバーロードがあります。 documentation on the GitHub pageはさらに詳細になります。

var mockHttp = new MockHttpMessageHandler(); 

// Setup a respond for the user api (including a wildcard in the URL) 
mockHttp.When("http://localhost/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 = async client.GetAsync("http://localhost/api/user/1234"); 
// or without async: var response = client.GetAsync(...).Result; 

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

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

本当にこのライブラリが好きで、$ httpBackendとの類似点があります。私はこのアプローチを行っており、うまくいきます。 –

+0

ジェームズありがとう、嬉しいですね! –

+0

私たちは明らかに同じ行に沿って考えていました。私のユースケースは非常にシンプルで、これまでのところ正当化していないでしょうが、この分野で多くの作業をしていれば、そのようなライブラリはリアルタイムの節約になると思います。私はこのようなものが必要な次回のためにプロジェクトをブックマークするつもりです。 –

関連する問題