HttpClient
を使用するクラスを単体テストするのが好きです。クラスコンストラクターにHttpClient
オブジェクトを挿入しました。
public class ClassA : IClassA
{
private readonly HttpClient _httpClient;
public ClassA(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<HttpResponseMessage> SendRequest(SomeObject someObject)
{
//Do some stuff
var request = new HttpRequestMessage(HttpMethod.Post, "http://some-domain.in");
//Build the request
var response = await _httpClient.SendAsync(request);
return response;
}
}
ここで、ClassA.SendRequest
メソッドの単体テストを行います。ユニットテストフレームワークにはMs Test
を、モッキングにはMoq
を使用しています。
HttpClient
をモックしようとすると、NotSupportedException
がスローされます。
[TestMethod]
public async Task SendRequestAsync_Test()
{
var mockHttpClient = new Mock<HttpClient>();
mockHttpClient.Setup(
m => m.SendAsync(It.IsAny<HttpRequestMessage>()))
.Returns(() => Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)));
}
この問題をどのように解決できますか?
この特定のオーバーロードメソッドは仮想ではないため、Moqでオーバーライドできません。
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);
これがNotSupportedException
をスローする理由です
あなたが探している仮想メソッドはこのメソッドです
public virtual Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
ただし、HttpClient
のモックは、内部メッセージハンドラの場合ほど簡単ではありません。
リクエストを偽装するときに柔軟性を高めるカスタムメッセージハンドラースタブを備えた具象クライアントを使用することをお勧めします。
委任ハンドラスタブの例を次に示します。
public class DelegatingHandlerStub : DelegatingHandler {
private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _handlerFunc;
public DelegatingHandlerStub() {
_handlerFunc = (request, cancellationToken) => Task.FromResult(request.CreateResponse(HttpStatusCode.OK));
}
public DelegatingHandlerStub(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handlerFunc) {
_handlerFunc = handlerFunc;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
return _handlerFunc(request, cancellationToken);
}
}
デフォルトのコンストラクターは基本的に、以前にモックしようとしていたことを実行していることに注意してください。また、リクエストのデリゲートを使用してカスタムシナリオを増やすこともできます。
スタブを使用すると、テストを次のようなものにリファクタリングできます
public async Task _SendRequestAsync_Test() {
//Arrange
var handlerStub = new DelegatingHandlerStub();
var client = new HttpClient(handlerStub);
var sut = new ClassA(client);
var obj = new SomeObject() {
//Populate
};
//Act
var response = await sut.SendRequest(obj);
//Assert
Assert.IsNotNull(response);
Assert.IsTrue(response.IsSuccessStatusCode);
}
HttpClientを使用した適切なモックは、ほとんどの人がdotnetでユニットテストを行う前に作成されたため、大変な作業です。リクエストURLに一致するパターンに基づいて返信定型文を返すスタブHTTPサーバーを設定することもあります。つまり、実際のHTTPリクエストをモックではなくローカルホストサーバーにテストします。 WireMock.net を使用すると、これが本当に簡単になり、ユニットテストのニーズのほとんどを満たすのに十分な速度で実行されます。
したがって、http://some-domain.in
の代わりに、いくつかのポートでlocalhostサーバーのセットアップを使用し、次のようにします。
var server = FluentMockServer.Start(/*server and port can be setup here*/);
server.Given(
Request.Create()
.WithPath("/").UsingPost()
)
.RespondWith(
Response.Create()
.WithStatusCode(200)
.WithHeader("Content-Type", "application/json")
.WithBody("{'attr':'value'}")
);
テストでのワイアモックの使用に関する詳細と ガイダンスはここにあります