Reputation: 18592
I have a Netty TCP Server with Spring Boot 2.3.1
.
I have been looking for many resources for testing handlers for Netty, like
However, it didn't work for me.
For EmbeddedChannel I have following error - Your remote address is embedded.
Here is code:
@ActiveProfiles("test")
@RunWith(MockitoJUnitRunner.class)
public class ProcessingHandlerTest_Embedded {
@Mock
private PermissionService permissionService;
private EmbeddedChannel embeddedChannel;
private final Gson gson = new Gson();
private ProcessingHandler processingHandler;
@Before
public void setUp() {
processingHandler = new ProcessingHandler(permissionService);
embeddedChannel = new EmbeddedChannel(processingHandler);
}
@Test
public void testHeartbeatMessage() {
// given
HeartbeatRequest heartbeatMessage = HeartbeatRequest.builder()
.messageID("heartbeat")
.build();
HeartbeatResponse response = HeartbeatResponse.builder()
.responseCode("ok")
.build();
String request = gson.toJson(heartbeatMessage).concat("\r\n");
String expected = gson.toJson(response).concat("\r\n");
// when
embeddedChannel.writeInbound(request);
// then
Queue<Object> outboundMessages = embeddedChannel.outboundMessages();
assertEquals(expected, outboundMessages.poll());
}
}
Output:
22:21:29.062 [main] INFO handler.ProcessingHandler - CLIENT_IP: embedded
22:21:29.062 [main] INFO handler.ProcessingHandler - CLIENT_REQUEST: {"messageID":"heartbeat"}
22:21:29.067 [main] DEBUG handler.ProcessingHandler - heartbeat request: HeartbeatRequest(messageID=heartbeat)
org.junit.ComparisonFailure:
<Click to see difference>
I tried something with a remote address:
@ActiveProfiles("test")
@RunWith(MockitoJUnitRunner.class)
public class ProcessingHandlerTest {
@Mock
private PermissionService permissionService;
private final Gson gson = new Gson();
private ProcessingHandler processingHandler;
@Mock
private ChannelHandlerContext channelHandlerContext;
@Mock
private Channel channel;
@Mock
private SocketAddress remoteAddress;
@Before
public void setUp() {
processingHandler = new ProcessingHandler(permissionService);
when(channelHandlerContext.channel()).thenReturn(channel);
when(channelHandlerContext.channel().remoteAddress()).thenReturn(remoteAddress);
}
@Test
public void testHeartbeatMessage() {
// given
HeartbeatRequest heartbeatMessage = HeartbeatRequest.builder()
.messageID("heartbeat")
.build();
HeartbeatResponse response = HeartbeatResponse.builder()
.responseCode("ok")
.build();
String request = gson.toJson(heartbeatMessage).concat("\r\n");
String expected = gson.toJson(response).concat("\r\n");
// when
processingHandler.channelRead(channelHandlerContext, request);
// then
}
}
Output:
22:26:06.119 [main] INFO handler.ProcessingHandler - CLIENT_IP: null
22:26:06.124 [main] INFO handler.ProcessingHandler - CLIENT_REQUEST: {"messageID":"heartbeat"}
22:26:06.127 [main] DEBUG handler.ProcessingHandler - heartbeat request: HeartbeatRequest(messageID=heartbeat)
However, I don't know how to do exact testing for such a case.
Here is a snippet from configuration:
@Bean
@SneakyThrows
public InetSocketAddress tcpSocketAddress() {
// for now, hostname is: localhost/127.0.0.1:9090
return new InetSocketAddress("localhost", nettyProperties.getTcpPort());
// for real client devices: A05264/172.28.1.162:9090
// return new InetSocketAddress(InetAddress.getLocalHost(), nettyProperties.getTcpPort());
}
@Component
@RequiredArgsConstructor
public class QrReaderChannelInitializer extends ChannelInitializer<SocketChannel> {
private final StringEncoder stringEncoder = new StringEncoder();
private final StringDecoder stringDecoder = new StringDecoder();
private final QrReaderProcessingHandler readerServerHandler;
private final NettyProperties nettyProperties;
@Override
protected void initChannel(SocketChannel socketChannel) {
ChannelPipeline pipeline = socketChannel.pipeline();
// Add the text line codec combination first
pipeline.addLast(new DelimiterBasedFrameDecoder(1024 * 1024, Delimiters.lineDelimiter()));
pipeline.addLast(new ReadTimeoutHandler(nettyProperties.getClientTimeout()));
pipeline.addLast(stringDecoder);
pipeline.addLast(stringEncoder);
pipeline.addLast(readerServerHandler);
}
}
The handler is a typical implementation of ChannelInboundHandlerAdapter
with overriding main methods.
How to test Handler with Spring Boot?
Upvotes: 0
Views: 2453
Reputation: 3363
So Your remote address is embedded.
isn't so much an error as it is, unexpected output which you're receiving.
There isn't anything special about how netty tests should be written for Spring Boot. As an example, here is a minimal test for a handler which always outputs "ok":
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.CharsetUtil;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringBootTest
class DemoApplicationTests {
@Test
void testRoundTrip() {
// given
String request = "heartbeat";
// when
EmbeddedChannel embeddedChannel = new EmbeddedChannel(new OkResponder());
embeddedChannel.writeInbound(request);
// then
ByteBuf outboundMessage = embeddedChannel.readOutbound();
//(ByteBuf)embeddedChannel.outboundMessages().poll(), as you've used, works in the above line too.
assertEquals("ok", outboundMessage.toString(CharsetUtil.UTF_8));
}
static class OkResponder extends ChannelInboundHandlerAdapter {
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.writeAndFlush(Unpooled.copiedBuffer("ok", CharsetUtil.UTF_8))
.addListener(ChannelFutureListener.CLOSE);
}
}
}
It looks to me that your ProcessingHandler
is calling ctx.channel().remoteAddress()
at some point which will return "embedded" for an EmbeddedChannel and that is causing the output that you're seeing.
Upvotes: 1