HttpClientFactoryをテストする方法
27378 ワード
いつでも働いているとき.NETアプリケーション、あなたが見る最も一般的なものの1つは依存性注入を使用して
しかし、ここに問題がある:モッキング
この記事では、我々はモックの方法を学びます
問題の紹介
完全に問題を理解するためには、具体的な例が必要です.
次のクラスは、入力文字列を指定して、削除HTTP呼び出しを使用してリモートクライアントに送信するメソッドを使用してサービスを実装します.
ご存知のように、新しいインスタンスを作成してはいけません
このアプローチには大きな問題があります.あなたは、単にmock
もちろん、我々は本物を使用しません
映画スタントダブルスとして嘲笑依存性を考えてください:あなたは、アクションシーンを実行しながら、あなたのメインスターを傷つけるためにしたくない.同様に、テストを実行するときにアプリケーションが実際の操作を実行する必要はありません.
私たちはメソッドをテストするためにmoqを使用し、HTTP呼び出しが正しく
IHttpClientFactoryのモックをMOQでつくる方法
モンクの作成のための完全なコードから始めましょう
完全にすべてのこれらのステートメントが意味するものを理解するためにそれを壊しましょう.
HttpMessageHandlerの操作
我々が会う最初の命令は
我々は何が起こるかに興味があるので
注意
次のステップ:モックドの動作を定義する
HttpMessageHandlerの動作定義
今、我々は使用するときに何が起こるかを定義する必要があります
我々がなぜそれを必要とするか、そして、次の操作の意味が何であるかを完全に理解するために、我々は
次の2つの詳細に注意してください. この名前を文字列として設定する方法を指定します
我々がパラメータの実際の値を気にしないと言うために、我々は使用します sendasyncがpublicなメソッドであれば、以下のようにしました.
次に、
HttpClientの作成
今のところ、我々は
IHttpClientFactoryインスタンスの設定
我々は最終的に作成する準備が整いました
IHttpClientFactoryによって実行される呼び出しを確認する方法
今、我々のテストでは、テスト中の操作を行ったと仮定します.
プロテクトインスタンスへのアクセス
既に見たように、HTTP操作を実行するオブジェクトは
それから、私たちは、何が起こったかを確認する必要があります
クエリ文字列のチェック
我々の主張の中心部分はこうです.
The
コードのリファクタリング
あなたのクラスのすべてのテストメソッドのためにそのコードを繰り返す必要があると想像してください.
だから我々はそれをリファクタリングすることができます
次に、アサーション部分を別のメソッドに移動できます.
更なる読書
🔗 Why we need HttpClientFactory | Microsoft Docs
🔗 HttpMessageHandler class | Microsoft Docs
🔗 Mock objects with static, complex data by using Manifest resources | Code4IT
🔗 Moq documentation | GitHub
🔗 How you can create extension methods in C# | Code4IT
ラッピング
この記事では、依存しているサービスをテストするためにどれだけトリッキーになるかを見ました
モッキング
そこにたくさんのnugetパッケージがあります.あなたのお気に入りは何ですか?
ハッピーコーディング!
🐧
IHttpClientFactory
サービスのコンストラクタへのインスタンス.もちろん、そのサービスをテストする必要があります.良い単体テストを書くためには、依存性を模擬して行動を完全に制御するのが良い習慣です.mock依存性へのよく知られたライブラリはmoqですそれを統合することはとても簡単ですIMyService
, あなたはそれを使用してそれの塊を作成することができますMock<IMyService>
.しかし、ここに問題がある:モッキング
IHttpClientFactory
それは単純ではありませんMock<IHttpClientFactory>
十分ではない.この記事では、我々はモックの方法を学びます
IHttpClientFactory
依存、どのようにHTTPの呼び出しのための動作を定義するには、最後に、我々は深く依存してモンクを許可するモスクの高度な機能に飛び込む.レッツゴー!問題の紹介
完全に問題を理解するためには、具体的な例が必要です.
次のクラスは、入力文字列を指定して、削除HTTP呼び出しを使用してリモートクライアントに送信するメソッドを使用してサービスを実装します.
public class MyExternalService
{
private readonly IHttpClientFactory _httpClientFactory;
public MyExternalService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task DeleteObject(string objectName)
{
string path = $"/objects?name={objectName}";
var client = _httpClientFactory.CreateClient("ext_service");
var httpResponse = await client.DeleteAsync(path);
httpResponse.EnsureSuccessStatusCode();
}
}
注目すべき重要な点は、私たちがIHttpClientFactory
; 我々はまた、新しいを作成しているHttpClient
使用するたびにそれが必要です_httpClientFactory.CreateClient("ext_service")
.ご存知のように、新しいインスタンスを作成してはいけません
HttpClient
ソケットの消耗の危険性を避けるために毎回オブジェクトを参照するlinks below ).このアプローチには大きな問題があります.あなたは、単にmock
IHttpClientFactory
依存関係ですが、手動でHttpClient
そして、その内臓を追跡する.もちろん、我々は本物を使用しません
IHttpClientFactory
インスタンス:私たちは実際のHTTP呼び出しを実行するアプリケーションを望まない.依存関係をモックする必要があります.映画スタントダブルスとして嘲笑依存性を考えてください:あなたは、アクションシーンを実行しながら、あなたのメインスターを傷つけるためにしたくない.同様に、テストを実行するときにアプリケーションが実際の操作を実行する必要はありません.
私たちはメソッドをテストするためにmoqを使用し、HTTP呼び出しが正しく
objectName
クエリ文字列内の変数.IHttpClientFactoryのモックをMOQでつくる方法
モンクの作成のための完全なコードから始めましょう
IHttpClientFactory
svar handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
HttpResponseMessage result = new HttpResponseMessage();
handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(result)
.Verifiable();
var httpClient = new HttpClient(handlerMock.Object) {
BaseAddress = new Uri("https://www.code4it.dev/")
};
var mockHttpClientFactory = new Mock<IHttpClientFactory>();
mockHttpClientFactory.Setup(_ => _.CreateClient("ext_service")).Returns(httpClient);
service = new MyExternalService(mockHttpClientFactory.Object);
たくさんのものが進んでいますね.完全にすべてのこれらのステートメントが意味するものを理解するためにそれを壊しましょう.
HttpMessageHandlerの操作
我々が会う最初の命令は
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
それはどういう意味ですか.HttpMessageHandler
すべてのHTTPリクエストの基本的な部分です.指定されたエンドポイントにsendasync呼び出しを行い、HttpRequestMessage
パラメータとして渡されるオブジェクトです.我々は何が起こるかに興味があるので
HttpMessageHandler
, 我々はそれをモックアップし、結果を変数に格納する必要があります.注意
MockBehavior.Strict
? これはオプションのパラメータであり、対応する設定がない場合には例外をスローします.試してみると、その引数をコンストラクタに削除し、handlerMock.Setup()
Part :テストを実行すると、型のエラーが表示されますMoq.MockException
.次のステップ:モックドの動作を定義する
HttpMessageHandler
HttpMessageHandlerの動作定義
今、我々は使用するときに何が起こるかを定義する必要があります
handlerMock
HTTP操作中のオブジェクト:HttpResponseMessage result = new HttpResponseMessage();
handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(result)
.Verifiable();
私たちが出会う最初のことはProtected()
. なぜ?我々がなぜそれを必要とするか、そして、次の操作の意味が何であるかを完全に理解するために、我々は
HttpMessageHandler
:// Summary: A base type for HTTP message handlers.
public abstract class HttpMessageHandler : IDisposable
{
/// Other stuff here...
// Summary: Send an HTTP request as an asynchronous operation.
protected internal abstract Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken);
}
この断片から、私たちは、我々に方法があるのを見ることができます.SendAsync
, はHttpRequestMessage
オブジェクトとCancellationToken
, HTTPリクエストを扱うものです.しかし、このメソッドは保護されています.したがって、私たちはProtected()
の保護されたメソッドにアクセスするにはHttpMessageHandler
クラス名とパラメータを使用して設定しなければなりませんSetup
メソッド.次の2つの詳細に注意してください.
ItExpr
の代わりにIt
我々は保護されたメンバーのセットアップを扱っているので.handlerMock
.Setup(_ => _.SendAsync(
It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>())
);
しかし、それが保護された方法であるので、我々は前にリストした方法を使う必要があります.次に、
SendAsync
型のオブジェクトを返すHttpResponseMessage
: ここでは、応答の内容については気にしないでください.そうすれば、私たちは、更なるカスタマイズなしでこのようにそれを残すことができます.HttpClientの作成
今のところ、我々は
HttpMessageHandler
オブジェクト、我々はそれを渡すことができますHttpClient
の新しいインスタンスを作成するコンストラクタHttpClient
それは我々が必要とする行為.var httpClient = new HttpClient(handlerMock.Object) {
BaseAddress = new Uri("https://www.code4it.dev/")
};
ここでは、値を設定しましたBaseAddress
HTTPコールを実行する際にnull参照を避ける有効なURIに対するプロパティ.非既存のURLを使用することができます:重要なことは、URLがよく形成される必要がありますです.IHttpClientFactoryインスタンスの設定
我々は最終的に作成する準備が整いました
IHttpClientFactory
!var mockHttpClientFactory = new Mock<IHttpClientFactory>();
mockHttpClientFactory.Setup(_ => _.CreateClient("ext_service")).Returns(httpClient);
var service = new MyExternalService(mockHttpClientFactory.Object);
だから、私たちのモックを作成しますIHttpClientFactory
のインスタンスを定義するHttpClient
を返します.CreateClient("ext_service")
. 最後に、我々はIHttpClientFactory
のコンストラクタにMyExternalService
.IHttpClientFactoryによって実行される呼び出しを確認する方法
今、我々のテストでは、テスト中の操作を行ったと仮定します.
// setup IHttpClientFactory
await service.DeleteObject("my-name");
どうすれば我々はHttpClient
実際にクエリ文字列の"my name "との終点と呼ばれますか?前と同様に、コード全体を見てみましょう.// verify that the query string contains "my-name"
handlerMock.Protected()
.Verify(
"SendAsync",
Times.Exactly(1), // we expected a single external request
ItExpr.Is<HttpRequestMessage>(req =>
req.RequestUri.Query.Contains("my-name")// Query string contains my-name
),
ItExpr.IsAny<CancellationToken>()
);
プロテクトインスタンスへのアクセス
既に見たように、HTTP操作を実行するオブジェクトは
HttpMessageHandler
, ここで私たちはmockkedし、格納されているhandlerMock
変数.それから、私たちは、何が起こったかを確認する必要があります
SendAsync
メソッドです.保護メソッドですしたがって、私たちはProtected
そのメンバにアクセスする.クエリ文字列のチェック
我々の主張の中心部分はこうです.
ItExpr.Is<HttpRequestMessage>(req =>
req.RequestUri.Query.Contains("my-name")// Query string contains my-name
),
再び、我々はprotected
メンバーなので、使用する必要がありますItExpr
の代わりにIt
.The
Is<HttpRequestMessage>
メソッドは関数を受け入れますFunc<HttpRequestMessage, bool>
これは、HttpRequestMessage
テストの下で-私たちのケースではreq
- 指定した述語にマッチします.もしそうならば、テストは通ります.コードのリファクタリング
あなたのクラスのすべてのテストメソッドのためにそのコードを繰り返す必要があると想像してください.
だから我々はそれをリファクタリングすることができます
HttpMessageHandler
へのモックSetUp
メソッド:[SetUp]
public void Setup()
{
this.handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
HttpResponseMessage result = new HttpResponseMessage();
this.handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.Returns(Task.FromResult(result))
.Verifiable()
;
var httpClient = new HttpClient(handlerMock.Object) {
BaseAddress = new Uri("https://www.code4it.dev/")
};
var mockHttpClientFactory = new Mock<IHttpClientFactory>();
mockHttpClientFactory.Setup(_ => _.CreateClient("ext_service")).Returns(httpClient);
this.service = new MyExternalService(mockHttpClientFactory.Object);
}
そして、handlerMock
and service
一部の個人的なメンバーで.次に、アサーション部分を別のメソッドに移動できます.
public static void Verify(this Mock<HttpMessageHandler> mock, Func<HttpRequestMessage, bool> match)
{
mock.Protected().Verify(
"SendAsync",
Times.Exactly(1), // we expected a single external request
ItExpr.Is<HttpRequestMessage>(req => match(req)
),
ItExpr.IsAny<CancellationToken>()
);
}
それで、我々のテストがちょうど一束の線に単純化されることができます:[Test]
public async Task Method_Should_ReturnSomething_When_Condition()
{
//Arrange occurs in the SetUp phase
//Act
await service.DeleteObject("my-name");
//Assert
handlerMock.Verify(r => r.RequestUri.Query.Contains("my-name"));
}
更なる読書
🔗 Why we need HttpClientFactory | Microsoft Docs
🔗 HttpMessageHandler class | Microsoft Docs
🔗 Mock objects with static, complex data by using Manifest resources | Code4IT
🔗 Moq documentation | GitHub
🔗 How you can create extension methods in C# | Code4IT
ラッピング
この記事では、依存しているサービスをテストするためにどれだけトリッキーになるかを見ました
IHttpClientFactory
インスタンス.幸運にも、我々は依存関係をモックするためにmoqのようなツールに頼ることができて、それらの依存のふるまいを完全に制御します.モッキング
IHttpClientFactory
難しい.しかし、ここで我々は、それらの難しさを克服し、我々のテストを書くのが簡単で理解する方法を見つけました.そこにたくさんのnugetパッケージがあります.あなたのお気に入りは何ですか?
ハッピーコーディング!
🐧
Reference
この問題について(HttpClientFactoryをテストする方法), 我々は、より多くの情報をここで見つけました https://dev.to/bellons91/how-to-test-httpclientfactory-with-moq-560eテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol