EmbeddedChannelを使用したNettyのテスト
1. 序章
この記事では、 EmbeddedChannel を使用して、インバウンドおよびアウトバウンドのチャネルハンドラーの機能をテストする方法を説明します。
Netty は、高性能の非同期アプリケーションを作成するための非常に用途の広いフレームワークです。 このようなアプリケーションの単体テストは、適切なツールがないと難しい場合があります。
ありがたいことに、フレームワークは EmbeddedChannelクラスを提供します。これにより、ChannelHandlersのテストが容易になります。
2. 設定
EmbeddedChannel はNettyフレームワークの一部であるため、必要な依存関係はNetty自体の依存関係のみです。
依存関係はMavenCentralにあります。
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.24.Final</version>
</dependency>
3. EmbeddedChannelOverview
EmbeddedChannelclassは、AbstractChannel の単なる別の実装であり、実際のネットワーク接続を必要とせずにデータを転送します。
これは、インバウンドチャネルにデータを書き込んで着信メッセージをシミュレートし、アウトバウンドチャネルで生成された応答を確認できるため便利です。 このようにして、各ChannelHandlerまたはチャネルパイプライン全体を個別にテストできます。
1つ以上のChannelHandlers、 をテストするには、最初に、コンストラクターの1つを使用してEmbeddedChannelインスタンスを作成する必要があります。
EmbeddedChannel を初期化する最も一般的な方法は、ChannelHandlersのリストをコンストラクターに渡すことです。
EmbeddedChannel channel = new EmbeddedChannel(
new HttpMessageHandler(), new CalculatorOperationHandler());
ハンドラーがパイプラインに挿入される順序をより細かく制御したい場合は、デフォルトのコンストラクターを使用して EmbeddedChannel を作成し、ハンドラーを直接追加できます。
channel.pipeline()
.addFirst(new HttpMessageHandler())
.addLast(new CalculatorOperationHandler());
また、
接続タイムアウト値をデフォルト値から下げるなど、カスタム構成を使用する場合は、 config()メソッドを使用してChannelConfigオブジェクトにアクセスできます。
DefaultChannelConfig channelConfig = (DefaultChannelConfig) channel
.config();
channelConfig.setConnectTimeoutMillis(500);
EmbeddedChannel には、ChannelPipelineへのデータの読み取りと書き込みに使用できるメソッドが含まれています。 最も一般的に使用される方法は次のとおりです。
- readInbound()
- readOutbound()
- writeInbound(Object…msgs)
- writeOutbound(Object…msgs)
読み取りメソッドは、インバウンド/アウトバウンドキューの最初の要素を取得して削除します。要素を削除せずにメッセージのキュー全体にアクセスする必要がある場合は、 outboundMessages()を使用できます。方法:
Object lastOutboundMessage = channel.readOutbound();
Queue<Object> allOutboundMessages = channel.outboundMessages();
メッセージがチャネルのインバウンド/アウトバウンドパイプラインに正常に追加された場合、書き込みメソッドはtrueを返します。
channel.writeInbound(httpRequest)
アイデアは、アウト ChannelHandlers がメッセージを処理し、結果がアウトバウンドパイプラインから読み取れることを期待するように、インバウンドパイプラインにメッセージを書き込むことです。
4. ChannelHandlersのテスト
HTTPリクエストを受信し、計算結果を含むHTTPレスポンスを期待する2つのChannelHandlersで構成されるパイプラインをテストする簡単な例を見てみましょう。
EmbeddedChannel channel = new EmbeddedChannel(
new HttpMessageHandler(), new CalculatorOperationHandler());
最初のHttpMessageHandlerは、HTTPリクエストからデータを抽出し、パイプラインの秒ChannelHandlerに渡してCalculatorOperationHandler、データを処理します。
それでは、HTTPリクエストを作成して、インバウンドパイプラインがそれを処理するかどうかを確認しましょう。
FullHttpRequest httpRequest = new DefaultFullHttpRequest(
HttpVersion.HTTP_1_1, HttpMethod.GET, "/calculate?a=10&b=5");
httpRequest.headers().add("Operator", "Add");
assertThat(channel.writeInbound(httpRequest)).isTrue();
long inboundChannelResponse = channel.readInbound();
assertThat(inboundChannelResponse).isEqualTo(15);
writeInbound()メソッドを使用してインバウンドパイプラインでHTTPリクエストを送信し、 readInbound()で結果を読み取ったことがわかります。 inboundChannelResponse は、インバウンドパイプラインのすべてのChannelHandlersによって処理された後に送信されたデータから生じたメッセージです。
次に、Nettyサーバーが正しいHTTP応答メッセージで応答するかどうかを確認しましょう。 これを行うには、メッセージがアウトバウンドパイプラインに存在するかどうかを確認します。
assertThat(channel.outboundMessages().size()).isEqualTo(1);
この場合、アウトバウンドメッセージはHTTP応答なので、内容が正しいかどうかを確認しましょう。 これを行うには、アウトバウンドパイプラインの最後のメッセージを読み取ります。
FullHttpResponse httpResponse = channel.readOutbound();
String httpResponseContent = httpResponse.content()
.toString(Charset.defaultCharset());
assertThat(httpResponseContent).isEqualTo("15");
4. 例外処理のテスト
もう1つの一般的なテストシナリオは、例外処理です。
exceptionCaught()メソッドを実装することで、 ChannelInboundHandlers で例外を処理できますが、例外を処理したくない場合は、代わりに次のように渡します。パイプラインの次のChannelHandler。
EmbeddedChannelクラスのcheckException()メソッドを使用して、 Throwable オブジェクトがパイプラインで受信されたかどうかを確認し、再スローできます。
このようにして、例外をキャッチし、ChannelHandlerがそれをスローする必要があるかどうかを確認できます。
assertThatThrownBy(() -> {
channel.pipeline().fireChannelRead(wrongHttpRequest);
channel.checkException();
}).isInstanceOf(UnsupportedOperationException.class)
.hasMessage("HTTP method not supported");
上記の例では、Exceptionをトリガーする予定のHTTPリクエストを送信したことがわかります。 checkException()メソッドを使用することにより、パイプラインに存在する最後の例外を再スローできるため、パイプラインから必要なものをアサートできます。
5. 結論
EmbeddedChannel は、Nettyフレームワークによって提供される優れた機能であり、ChannelHandlerパイプラインの正確性をテストするのに役立ちます。 これを使用して、各 ChannelHandler を個別に、さらに重要なことにパイプライン全体をテストできます。
この記事のソースコードは、GitHubでから入手できます。