Testando o Netty com EmbeddedChannel

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 allOutboundMessages = channel.outboundMessages();


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.