厳密には、HttpClient が内部で最終的に呼び出す HttpMessageHandler を、Moq を使ってモック化する。
まずは HttpMessageHandler のインタフェースを、存在しないのででっち上げる。
public interface IHttpMessageHandler { Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken); }
HttpMessageHandler.SendAsync は protected internal なので、一筋縄ではいかない。Moq が提供している protected なメンバーをモック化する機能を使う。
var handlerMock = new Mock<HttpMessageHandler>(); handlerMock.Protected() .As<IHttpMessageHandler>() .Setup(m => m.SendAsync( It.Is<HttpRequestMessage>(r => r.RequestUri.PathAndQuery.Contains("/api/account/login") && r.Method == HttpMethod.Post && r.Content.ReadAsStringAsync().Result.Contains("foobar") && r.Content.ReadAsStringAsync().Result.Contains("P@ssw0rd") ), It.IsAny<CancellationToken>())) .Returns(() => { var response = new HttpResponseMessage(HttpStatusCode.BadRequest); return Task.FromResult(response); });
ついでに IHttpClientFactory もモック化しておく。こちらは素直。
var factoryMock = new Mock<IHttpClientFactory>(); factoryMock.Setup(m => m.CreateClient(It.IsAny<string>())) .Returns(() => { return new HttpClient(handlerMock.Object, false); });
HttpMessageHandler を継承したテスト用クラスを定義するのが簡単だけど、他は Moq 使っているのに、HttpClient 関連だけ違うっていうのは気持ち悪いので、なんとかやってみた。