Tester Netty med EmbeddedChannel
1. Introduktion
I denne artikel kan vi se, hvordan du bruger EmbeddedChannel for at teste funktionaliteten af vores indgående og udgående kanalhandlere.
Netty er en meget alsidig ramme til at skrive højtydende asynkrone applikationer. Enhedstest af sådanne applikationer kan være vanskelig uden de rigtige værktøjer.
Heldigvis giver rammen os den EmbeddedChannel klasse – som letter testningen af ChannelHandlers .
2. Opsætning
Den Embedded Channel er en del af Netty-rammen, så den eneste nødvendige afhængighed er den til Netty selv.
Afhængigheden kan findes på Maven Central:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.24.Final</version>
</dependency>
3. Indlejret kanal Oversigt
Den Embedded Channel klasse er blot endnu en implementering af AbstractChannel – som transporterer data uden behov for en rigtig netværksforbindelse .
Dette er nyttigt, fordi vi kan simulere indgående beskeder ved at skrive data på de indgående kanaler og også kontrollere det genererede svar på de udgående kanaler. På denne måde kan vi teste hver ChannelHandler individuelt eller i hele kanalpipelinen.
For at teste en eller flere ChannelHandlers , vi skal først oprette en EmbeddedChannel instans ved hjælp af en af dens konstruktører.
Den mest almindelige måde at initialisere en EmbeddedChannel er ved at videregive listen over ChannelHandlers til sin konstruktør:
EmbeddedChannel channel = new EmbeddedChannel(
new HttpMessageHandler(), new CalculatorOperationHandler());
Hvis vi ønsker at have mere kontrol over den ordre, som behandlerne indsættes i pipelinen, kan vi oprette en EmbeddedChannel med standardkonstruktøren og tilføj handlerne direkte:
channel.pipeline()
.addFirst(new HttpMessageHandler())
.addLast(new CalculatorOperationHandler());
Også når vi opretter en Embedded Channel det vil have en standardkonfiguration givet af DefaultChannelConfig klasse.
Når vi vil bruge en tilpasset konfiguration, som at sænke timeoutværdien for forbindelsen fra standardværdien, kan vi få adgang til ChannelConfig objekt ved at bruge config() metode:
DefaultChannelConfig channelConfig = (DefaultChannelConfig) channel
.config();
channelConfig.setConnectTimeoutMillis(500);
Den EmbeddedChannel omfatter metoder, som vi kan bruge til at læse og skrive data til vores ChannelPipeline . De mest brugte metoder er:
- readInbound()
- readOutbound()
- writeInbound(Object… msgs)
- writeOutbound(Object… msgs)
Læsemetoderne henter og fjerner det første element i den indgående/udgående kø. Når vi har brug for adgang til hele køen af beskeder uden at fjerne noget element, kan vi bruge outboundMessages() metode:
Object lastOutboundMessage = channel.readOutbound();
Queue<Object> allOutboundMessages = channel.outboundMessages();
Skrivemetoderne returnerer true når meddelelsen blev tilføjet til den indgående/udgående pipeline på Kanalen:
channel.writeInbound(httpRequest)
Tanken er, at vi skriver beskeder på den indgående pipeline, så de ude ChannelHandlers behandler dem, og vi forventer, at resultatet kan læses fra den udgående pipeline.
4. Test af ChannelHandlers
Lad os se på et simpelt eksempel, hvor vi vil teste en pipeline sammensat af to ChannelHandlers der modtager en HTTP-anmodning og forventer et HTTP-svar, der indeholder resultatet af en beregning:
EmbeddedChannel channel = new EmbeddedChannel(
new HttpMessageHandler(), new CalculatorOperationHandler());
Den første, HttpMessageHandler vil udtrække dataene fra HTTP-anmodningen og videregive dem til sekunders ChannelHandler i pipelinen, CalculatorOperationHandler , for at behandle dataene.
Lad os nu skrive HTTP-anmodningen og se, om den indgående pipeline behandler den:
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);
Vi kan se, at vi har sendt HTTP-anmodningen på den indgående pipeline ved hjælp af writeInbound() metode og læs resultatet med readInbound(); inboundChannelResponse er den besked, der stammer fra de data, vi har sendt, efter at de blev behandlet af alle ChannelHandlers i den indgående pipeline.
Lad os nu tjekke, om vores Netty-server svarer med den korrekte HTTP-svarmeddelelse. For at gøre dette kontrollerer vi, om der findes en meddelelse på den udgående pipeline:
assertThat(channel.outboundMessages().size()).isEqualTo(1);
Den udgående meddelelse er i dette tilfælde et HTTP-svar, så lad os tjekke, om indholdet er korrekt. Det gør vi ved at læse den sidste besked i den udgående pipeline:
FullHttpResponse httpResponse = channel.readOutbound();
String httpResponseContent = httpResponse.content()
.toString(Charset.defaultCharset());
assertThat(httpResponseContent).isEqualTo("15");
4. Test af undtagelseshåndtering
Et andet almindeligt testscenarie er undtagelseshåndtering.
Vi kan håndtere undtagelser i vores ChannelInboundHandlers ved at implementere exceptionCaught() metode, men der er nogle tilfælde, hvor vi ikke ønsker at håndtere en undtagelse, og i stedet sender vi den videre til den næste ChannelHandler i pipelinen.
Vi kan bruge checkException() metode fra EmbeddedChannel klasse for at kontrollere, om nogen kan kastes objekt blev modtaget på rørledningen og kaster det igen.
På denne måde kan vi fange undtagelsen og kontroller, om ChannelHandler burde eller burde ikke have smidt det:
assertThatThrownBy(() -> {
channel.pipeline().fireChannelRead(wrongHttpRequest);
channel.checkException();
}).isInstanceOf(UnsupportedOperationException.class)
.hasMessage("HTTP method not supported");
Vi kan se i eksemplet ovenfor, at vi har sendt en HTTP-anmodning, som vi forventer vil udløse en undtagelse . Ved at bruge checkException() metode, kan vi omkaste den sidste undtagelse, der eksisterer i pipelinen, så vi kan hævde, hvad der er nødvendigt fra det.
5. Konklusion
Den EmbeddedChannel er en fantastisk funktion leveret af Netty-rammen for at hjælpe os med at teste rigtigheden af ChannelHandler rørledning. Den kan bruges til at teste hver ChannelHandler individuelt og endnu vigtigere hele pipelinen.
Kildekoden til artiklen er tilgængelig på GitHub.