Тестирование Netty с EmbeddedChannel
1. Вступление
В этой статье мы увидим, как использоватьEmbeddedChannel to для проверки функциональности наших обработчиков входящего и исходящего каналов.
Netty - очень универсальная среда для написания высокопроизводительных асинхронных приложений. Модульное тестирование таких приложений может быть сложно без правильных инструментов.
К счастью, фреймворк предоставляет намEmbeddedChannel class – which facilitates the testing of ChannelHandlers.
2. Настроить
EmbeddedChannel является частью инфраструктуры Netty, поэтому единственная необходимая зависимость - это зависимость для самой Netty.
Зависимость можно найти вMaven Central:
io.netty
netty-all
4.1.24.Final
3. EmbeddedChannelOverviewс
EmbeddedChannelclass is just another implementation of AbstractChannel –, который передает данныеwithout the need for a real network connection.
Это полезно, потому что мы можем моделировать входящие сообщения, записывая данные по входящим каналам, а также проверять сгенерированный ответ на исходящих каналах. Таким образом, мы можем индивидуально протестировать каждыйChannelHandler сор во всем конвейере канала.
Чтобы протестировать один или несколькоChannelHandlers, we сначала должен создать синстанциюEmbeddedChannel , используя один из своих конструкторов.
Самый распространенный способ инициализироватьEmbeddedChannel is путем передачи спискаChannelHandlers to его конструктору:
EmbeddedChannel channel = new EmbeddedChannel(
new HttpMessageHandler(), new CalculatorOperationHandler());
Если мы хотим иметь больший контроль над порядком вставки обработчиков в конвейер, мы можем создатьEmbeddedChannel с конструктором по умолчанию и напрямую добавить обработчики:
channel.pipeline()
.addFirst(new HttpMessageHandler())
.addLast(new CalculatorOperationHandler());
Такжеwhen we create an EmbeddedChannel, it’ll have a default configuration given by the DefaultChannelConfigclass.
Когда мы хотим использовать настраиваемую конфигурацию, например, уменьшить значение тайм-аута подключения по сравнению с значением по умолчанию, мы можем получить доступ к объектуChannelConfig object, используя методconfig():
DefaultChannelConfig channelConfig = (DefaultChannelConfig) channel
.config();
channelConfig.setConnectTimeoutMillis(500);
EmbeddedChannel включает методы, которые мы можем использовать для чтения и записи данных в нашChannelPipeline. Наиболее часто используемые методы:
-
readInbound ()
-
readOutbound ()
-
writeInbound (Объект… сообщения)
-
writeOutbound (Объект… сообщения)
The read methods retrieve and remove the first element in the inbound/outbound queue.Если нам нужен доступ ко всей очереди сообщений без удаления какого-либо элемента, мы можем использовать методoutboundMessages() :
Object lastOutboundMessage = channel.readOutbound();
Queue
Методы записи возвращаютtrue w, если сообщение было успешно добавлено во входящий / исходящий конвейерChannel:.
channel.writeInbound(httpRequest)
Идея состоит в том, что мы пишем сообщения во входящий конвейер, чтобы outChannelHandlerswill их обрабатывал, и мы ожидаем, что результат будет доступен для чтения из исходящего конвейера.
4. ТестированиеChannelHandlers
Давайте рассмотрим простой пример, в котором мы хотим протестировать конвейер, состоящий из двухChannelHandlers , которые получают HTTP-запрос и ожидают ответа HTTP, содержащего результат вычисления:
EmbeddedChannel channel = new EmbeddedChannel(
new HttpMessageHandler(), new CalculatorOperationHandler());
Первый,HttpMessageHandler w, извлекает данные из 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);
Мы видим, что мы отправили HTTP-запрос во входящий конвейер с помощью методаwriteInbound() и прочитали результат с помощью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. Тестирование обработки исключений
Другой распространенный сценарий тестирования - обработка исключений.
Мы можем обрабатывать исключения в нашемChannelInboundHandlers , реализуя методexceptionCaught() , но есть некоторые случаи, когда мы не хотим обрабатывать исключение, и вместо этого мы передаем его следующемуChannelHandlerin трубопровод.
Мы можем использовать методcheckException() из классаEmbeddedChannel, чтобы проверить, был ли получен какой-либо объектThrowable object по конвейеру, и повторно его выбросить.
Таким образом, мы можем пойматьException and check, должен или не должен былChannelHandlerбросать его:
assertThatThrownBy(() -> {
channel.pipeline().fireChannelRead(wrongHttpRequest);
channel.checkException();
}).isInstanceOf(UnsupportedOperationException.class)
.hasMessage("HTTP method not supported");
В приведенном выше примере мы видим, что отправили HTTP-запрос, который, как мы ожидаем, вызоветException. Используя методcheckException() , мы можем повторно вызвать последнее исключение, которое существует в конвейере, чтобы мы могли утверждать, что от него требуется.
5. Заключение
EmbeddedChannel - отличная функция, предоставляемая фреймворком Netty, чтобы помочь нам проверить правильность spipeline outChannelHandler . Его можно использовать для тестирования каждогоChannelHandler по отдельности и, что более важно, всего конвейера.
Исходный код статьи доступенover on GitHub.