Testando o Netty com EmbeddedChannel
1. Introdução
Neste artigo, veremos como usarEmbeddedChannel para testar a funcionalidade de nossos manipuladores de canal de entrada e saída.
Netty é uma estrutura muito versátil para escrever aplicativos assíncronos de alto desempenho. O teste de unidade desses aplicativos pode ser complicado sem as ferramentas certas.
Felizmente, a estrutura nos forneceEmbeddedChannel class – which facilitates the testing of ChannelHandlers.
2. Configuração
OEmbeddedChannel é parte do framework Netty, então a única dependência necessária é aquela do próprio Netty.
A dependência pode ser encontrada emMaven Central:
io.netty
netty-all
4.1.24.Final
3. EmbeddedChannelOverview
OEmbeddedChannelclass is just another implementation of AbstractChannel – que transporta dadoswithout the need for a real network connection.
Isso é útil porque podemos simular mensagens recebidas gravando dados nos canais de entrada e também verificando a resposta gerada nos canais de saída. Desta forma, podemos testar individualmente cada sorChannelHandler em todo o pipeline do canal.
Para testar um ou maisChannelHandlers, we primeiro tem que criar uma instânciaEmbeddedChannel usando um de seus construtores.
A maneira mais comum de inicializar umEmbeddedChannel é passando a lista deChannelHandlers para seu construtor:
EmbeddedChannel channel = new EmbeddedChannel(
new HttpMessageHandler(), new CalculatorOperationHandler());
Se quisermos ter mais controle sobre a ordem em que os manipuladores são inseridos no pipeline, podemos criar umEmbeddedChannel com o construtor padrão e adicionar diretamente os manipuladores:
channel.pipeline()
.addFirst(new HttpMessageHandler())
.addLast(new CalculatorOperationHandler());
Além disso,when we create an EmbeddedChannel, it’ll have a default configuration given by the DefaultChannelConfigclass.
Quando queremos usar uma configuração personalizada, como diminuir o valor de tempo limite de conexão do padrão, podemos acessar o sobjetoChannelConfig usando o métodoconfig():
DefaultChannelConfig channelConfig = (DefaultChannelConfig) channel
.config();
channelConfig.setConnectTimeoutMillis(500);
OEmbeddedChannel inclui métodos que podemos usar para ler e gravar dados em nossoChannelPipeline. Os métodos mais usados são:
-
readInbound ()
-
readOutbound ()
-
writeInbound (objeto… msgs)
-
writeOutbound (Object… msgs)
The read methods retrieve and remove the first element in the inbound/outbound queue. Quando precisamos acessar toda a fila de mensagens sem remover nenhum elemento, podemos usar o métodooutboundMessages() :
Object lastOutboundMessage = channel.readOutbound();
Queue
Os métodos de gravação retornamtrue quando a mensagem foi adicionada com sucesso ao pipeline de entrada / saída deChannel:
channel.writeInbound(httpRequest)
A ideia é escrevermos mensagens no pipeline de entrada para que oChannelHandlersde saída as processe e esperamos que o resultado seja legível no pipeline de saída.
4. TestandoChannelHandlers
Vejamos um exemplo simples em que queremos testar um pipeline composto de doisChannelHandlers que recebem uma solicitação HTTP e esperam uma resposta HTTP que contém o resultado de um cálculo:
EmbeddedChannel channel = new EmbeddedChannel(
new HttpMessageHandler(), new CalculatorOperationHandler());
O primeiro,HttpMessageHandler , extrairá os dados da solicitação HTTP e os passará para os segundosChannelHandler do pipeline,CalculatorOperationHandler, para fazer o processamento com os dados.
Agora, vamos escrever a solicitação HTTP e ver se o pipeline de entrada a processa:
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);
Podemos ver que enviamos a solicitação HTTP no pipeline de entrada usando o métodowriteInbound() method e lemos o resultado comreadInbound(); inboundChannelResponse é a mensagem que resultou dos dados que enviamos depois de serem processados por todos osChannelHandlers do pipeline de entrada.
Agora, vamos verificar se nosso servidor Netty responde com a mensagem de resposta HTTP correta. Para fazer isso, verificaremos se existe uma mensagem no pipeline de saída:
assertThat(channel.outboundMessages().size()).isEqualTo(1);
A mensagem de saída, neste caso, é uma resposta HTTP, então vamos verificar se o conteúdo está correto. Fazemos isso lendo a última mensagem no pipeline de saída:
FullHttpResponse httpResponse = channel.readOutbound();
String httpResponseContent = httpResponse.content()
.toString(Charset.defaultCharset());
assertThat(httpResponseContent).isEqualTo("15");
4. Testando o tratamento de exceções
Outro cenário de teste comum é o tratamento de exceções.
Podemos lidar com exceções em nossoChannelInboundHandlers implementando o métodoexceptionCaught() , mas há alguns casos em que não queremos lidar com uma exceção e, em vez disso, passamos para o próximoChannelHandlerem o pipeline.
Podemos usar o métodocheckException() do sclassEmbeddedChannelpara verificar se algumThrowable objeto foi recebido no pipeline e relançá-lo.
Dessa forma, podemos pegar a areiaException para verificar seChannelHandler deve ou não ter jogado:
assertThatThrownBy(() -> {
channel.pipeline().fireChannelRead(wrongHttpRequest);
channel.checkException();
}).isInstanceOf(UnsupportedOperationException.class)
.hasMessage("HTTP method not supported");
Podemos ver no exemplo acima, que enviamos uma solicitação HTTP que esperamos acionar umException. Usando o métodocheckException() , podemos relançar a última exceção que existe no pipeline, para que possamos afirmar o que é necessário a partir dela.
5. Conclusão
OEmbeddedChannel é um ótimo recurso fornecido pelo framework Netty para nos ajudar a testar a exatidão de nosso spipelineChannelHandler . Ele pode ser usado para testar cadaChannelHandler individualmente e, mais importante, todo o pipeline.
O código-fonte do artigo está disponívelover on GitHub.