Netty提供了EmbeddedChannel,来建立内嵌的通道, 其并不与网络设置进行真正通信,适合我们来进行对ChannelHandler逻辑的单元测试。
EmbeddedChannel 的父类 AbstractEmbeddedChannel 提供以下方法:
writeInbound(..) : 将信息写入Channel, ChannelInboundHandler的实现将响应事件,写入成功返回true
readInbound(..) : 读取通过writeInbound()写入 Channel的数据,如果返回null,则无数据。
writeOutbound() : 将信息写入Channel , ChannelOutboundHandler的实现奖响应事件,写入成功返true
readOutbound() : 读取writeOutbound()写入Channel的数据。
finish() :返回true表示,写入Channel的数据已经完成。
关系图:
Codes 是 ChannlHandler的实现,下面些测试Decoding 跟 Encoding,分别对应Inbound跟Outbound
测试Inbound
FixedLengthFrameDecoder
public class FixedLengthFrameDecoder extends ByteToMessageDecoder { private final int frameLength; public FixedLengthFrameDecoder( int frameLength ) { if ( frameLength <= 0 ) { throw new IllegalArgumentException( "frameLength must be a positive integer:" + frameLength ); } this.frameLength = frameLength; } @Override protected void decode( ChannelHandlerContext ctx, ByteBuf in, List<Object> out ) throws Exception { while ( in.readableBytes() >= frameLength ) { ByteBuf buf = in.readBytes( frameLength ); out.add( buf ); } } }FixedLengthFrameDecoderTest
public class FixedLengthFrameDecoderTest { @Test public void testFramesDecoded() { ByteBuf buf = Unpooled.buffer(); for (int i = 0; i < 9; i++) { buf.writeByte(i); } ByteBuf input = buf.duplicate(); EmbeddedChannel channel = new EmbeddedChannel(new FixedLengthFrameDecoder(3)); Assert.assertFalse(channel.writeInbound(input.readBytes(2))); Assert.assertTrue(channel.writeInbound(input.readBytes(7))); Assert.assertTrue(channel.finish()); Assert.assertEquals(buf.readBytes(3), channel.readInbound()); Assert.assertEquals(buf.readBytes(3), channel.readInbound()); Assert.assertEquals(buf.readBytes(3), channel.readInbound()); } }
测试Outbond
AbsIntegerEncoder
public class AbsIntegerEncoder extends MessageToByteEncoder<ByteBuf> { @Override protected void encode( ChannelHandlerContext ctx, ByteBuf in, ByteBuf out ) throws Exception { while ( in.readableBytes() >= 4 ) { int value = Math.abs( in.readInt() ); out.writeInt( value ); System.out.println( "encoded value:" + value ); } } }AbsIntegerEncoderTest
public class AbsIntegerEncoderTest { @Test public void testEncoded() throws Exception { ByteBuf buf = Unpooled.buffer(); for ( int i = 0; i < 10; i++ ) { buf.writeInt( i * -1 ); } EmbeddedChannel channel = new EmbeddedChannel( new AbsIntegerEncoder() ); Assert.assertTrue( channel.writeOutbound( buf ) ); Assert.assertTrue( channel.finish() ); ByteBuf output = (ByteBuf) channel.readOutbound(); for ( int i = 0; i < 10; i++ ) { Assert.assertEquals( i, output.readInt() ); } Assert.assertFalse( output.isReadable() ); Assert.assertNull( channel.readOutbound() ); } }测试异常
FrameChunkDecoder
public class FrameChunkDecoder extends ByteToMessageDecoder { private final int maxFrameSize; public FrameChunkDecoder( int maxFrameSize ) { this.maxFrameSize = maxFrameSize; } @Override protected void decode( ChannelHandlerContext ctx, ByteBuf in, List<Object> out ) throws Exception { int readableBytes = in.readableBytes(); if ( readableBytes > maxFrameSize ) { in.clear(); throw new TooLongFrameException(); } ByteBuf buf = in.readBytes( readableBytes ); out.add( buf ); } }
FrameChunkDecoderTest
public class FrameChunkDecoderTest { @Test public void testFramesDecoded() throws Exception { ByteBuf buf = Unpooled.buffer(); for ( int i = 0; i < 10; i++ ) { buf.writeByte( i ); } ByteBuf input = buf.duplicate(); EmbeddedChannel channel = new EmbeddedChannel( new FrameChunkDecoder( 3 ) ); Assert.assertTrue( channel.writeInbound( input.readBytes( 2 ) ) ); try { channel.writeInbound( input.readBytes( 4 ) ); Assert.fail(); } catch ( TooLongFrameException e ) { } Assert.assertTrue( channel.writeInbound( input.readBytes( 3 ) ) ); Assert.assertTrue( channel.finish() ); Assert.assertEquals( buf.readBytes( 2 ), channel.readInbound() ); Assert.assertEquals( buf.skipBytes( 4 ).readBytes( 3 ), channel.readInbound() ); } }