netty基础09_利用EmbeddedChannel做单元测试
netty提供了一种特殊的Channel 实现——EmbeddedChannel用来测试ChannelHandler;
例如:想测试自己编写服务端解码器是否能正确解码;
想在程序中测试需要在客户端给服务端发送消息,不方便调试;
使用EmbeddedChannel可以做到本地调试解码器;
1.EmbeddedChannel的api
2.运行机制
可以在EmbeddedChannel的有参构造中添加ChannelHandler;
EmbeddedChannel channel = new EmbeddedChannel(new MyDecoder());
对于入站消息:
调用EmbeddedChannel的writeInbound方法将数据写入管道;
数据会被管道中的入站处理器处理;
然后可以调用readInbound方法的到处理后的数据,看处理后的数据是否符合预期;
对于出站消息:
和入站消息的处理方法类似,只不过调用的方法不同;
调用writeOutbound方法,将出站的数据写入管道;
数据会被管道中的出站处理器处理;
调用readOutbound方法可以得到处理后的数据,看是否被正确处理;
3.测试
工具类HexUtil:用来将字节数组转换成十六进制字符串;
public class HexUtil { private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; public static byte[] decode(char[] data) { int len = data.length; if ((len & 0x01) != 0) { throw new IllegalArgumentException("Odd number of characters."); } byte[] out = new byte[len >> 1]; // two characters form the hex value. for (int i = 0, j = 0; j < len; i++) { int f = toDigit(data[j], j) << 4; j++; f = f | toDigit(data[j], j); j++; out[i] = (byte) (f & 0xFF); } return out; } public static char[] encode(byte[] data) { return encode(data, DIGITS_UPPER); } private static char[] encode(byte[] data, char[] toDigits) { int l = data.length; char[] out = new char[l << 1]; // two characters form the hex value. for (int i = 0, j = 0; i < l; i++) { out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; out[j++] = toDigits[0x0F & data[i]]; } return out; } public static String encodeString(byte[] data) { return new String(encode(data)); } private static int toDigit(char ch, int index) { final int digit = Character.digit(ch, 16); if (digit == -1) { throw new IllegalArgumentException("Illegal hexadecimal character " + ch + " at index " + index); } return digit; } }
1)入站消息测试
目的:解码器会将字节数组解码成一帧4个字节的消息,看是否正确解码;
解码器:
public class MyDecoder extends ByteToMessageDecoder { @Override protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception { if(byteBuf.readableBytes() >= 4){ ByteBuf buf = byteBuf.readBytes(4); //从消息中读取4个字节,并放到消息列表 list.add(buf); } } }
测试类:可以用Junit来测试
@Test public void testInBound() throws Exception{ ByteBuf buf = Unpooled.buffer(); for(int i=0;i<6;i++){ buf.writeByte(i); } ByteBuf buf2 = Unpooled.buffer(); for(int i=0;i<10;i++){ buf2.writeByte(i+6); } EmbeddedChannel channel = new EmbeddedChannel(new MyDecoder()); //新建EmbeddedChannel对象用来测试解码器,构造方法中添加待测解码器实例 channel.writeInbound(buf); channel.writeInbound(buf2); channel.finish(); //总共将16个字节的数据写入到进站消息中 ByteBuf read = channel.readInbound(); //获取一条解码后数据,如果正确解码应该是前4个字节的数据; System.out.println(HexUtil.encodeString(ByteBufUtil.getBytes(read))); read.release(); read = channel.readInbound(); //再次获取解码后的数据,如果正确解码,应该是得到第5~8字节的数据 System.out.println(HexUtil.encodeString(ByteBufUtil.getBytes(read))); read.release(); read = channel.readInbound(); //9~12字节 System.out.println(HexUtil.encodeString(ByteBufUtil.getBytes(read))); read.release(); read = channel.readInbound(); //13~16字节 System.out.println(HexUtil.encodeString(ByteBufUtil.getBytes(read))); }
结果:可以看到4次获取解码后的消息,每次得到4个字节的数据,说明解码符合预期;
2)出站消息测试
编码器:继承自消息转消息编码器,将ByteBuf转为字符串
public class MyEncoder extends MessageToMessageEncoder<ByteBuf>{ @Override protected void encode(ChannelHandlerContext context, ByteBuf buf, List<Object> list) throws Exception { int size = buf.readableBytes(); while(buf.readableBytes() >= 4){ String hex = HexUtil.encodeString(ByteBufUtil.getBytes(buf.readBytes(4))); //每4个字节转换成一个字符串,存入出队消息列表中 list.add(hex); } } }
测试类:16个字节用编码器做消息转换,每4个字节转成一个字符串
@Test public void testOutBound(){ ByteBuf buf = Unpooled.buffer(); for(int i=0;i<16;i++){ buf.writeByte(i); } EmbeddedChannel channel = new EmbeddedChannel(new MyEncoder()); channel.writeOutbound(buf); channel.finish(); System.out.println((String)channel.readOutbound()); System.out.println((String)channel.readOutbound()); System.out.println((String)channel.readOutbound()); System.out.println((String)channel.readOutbound()); }
结果:将16字节的ByteBuf转换成了4个字符串