Netty如何測(cè)試 ChannelHandler

2018-08-08 10:55 更新

本節(jié)的內(nèi)容是介紹Netty中要如何通過(guò) EmbeddedChannel 對(duì) ChannelHandler 進(jìn)行測(cè)試。

測(cè)試入站消息

我們先來(lái)編寫一個(gè)簡(jiǎn)單的 ByteToMessageDecoder 實(shí)現(xiàn),在有足夠的數(shù)據(jù)可以讀取的情況下將產(chǎn)生固定大小的包,如果沒(méi)有足夠的數(shù)據(jù)可以讀取,則會(huì)等待下一個(gè)數(shù)據(jù)塊并再次檢查是否可以產(chǎn)生一個(gè)完整包。

如圖所示,它可能會(huì)占用一個(gè)以上的“event”以獲取足夠的字節(jié)產(chǎn)生一個(gè)數(shù)據(jù)包,并將它傳遞到 ChannelPipeline 中的下一個(gè) ChannelHandler,

Figure%2010

Figure 10.2 Decoding via FixedLengthFrameDecoder

實(shí)現(xiàn)如下:

Listing 10.1 FixedLengthFrameDecoder implementation

public class FixedLengthFrameDecoder extends ByteToMessageDecoder { //1

    private final int frameLength;

    public FixedLengthFrameDecoder(int frameLength) { //2
        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 {
        if (in.readableBytes() >= frameLength) { //3
            ByteBuf buf = in.readBytes(frameLength);//4
            out.add(buf); //5
        }
    }
}
  1. 繼承 ByteToMessageDecoder 用來(lái)處理入站的字節(jié)并將他們解碼為消息
  2. 指定產(chǎn)出的幀的長(zhǎng)度
  3. 檢查是否有足夠的字節(jié)用于讀到下個(gè)幀
  4. 從 ByteBuf 讀取新幀
  5. 添加幀到解碼好的消息 List

下面是單元測(cè)試的例子,使用 EmbeddedChannel

Listing 10.2 Test the FixedLengthFrameDecoder

public class FixedLengthFrameDecoderTest {

    @Test    //1
    public void testFramesDecoded() {
        ByteBuf buf = Unpooled.buffer(); //2
        for (int i = 0; i < 9; i++) {
            buf.writeByte(i);
        }
        ByteBuf input = buf.duplicate();

        EmbeddedChannel channel = new EmbeddedChannel(new FixedLengthFrameDecoder(3)); //3
        Assert.assertFalse(channel.writeInbound(input.readBytes(2))); //4
        Assert.assertTrue(channel.writeInbound(input.readBytes(7)));

        Assert.assertTrue(channel.finish());  //5
        ByteBuf read = (ByteBuf) channel.readInbound();
        Assert.assertEquals(buf.readSlice(3), read);
        read.release();

        read = (ByteBuf) channel.readInbound();
        Assert.assertEquals(buf.readSlice(3), read);
        read.release();

        read = (ByteBuf) channel.readInbound();
        Assert.assertEquals(buf.readSlice(3), read);
        read.release();

        Assert.assertNull(channel.readInbound());
        buf.release();
    }


    @Test
    public void testFramesDecoded2() {
        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());
        ByteBuf read = (ByteBuf) channel.readInbound();
        Assert.assertEquals(buf.readSlice(3), read);
        read.release();

        read = (ByteBuf) channel.readInbound();
        Assert.assertEquals(buf.readSlice(3), read);
        read.release();

        read = (ByteBuf) channel.readInbound();
        Assert.assertEquals(buf.readSlice(3), read);
        read.release();

        Assert.assertNull(channel.readInbound());
        buf.release();
    }
}
  1. 測(cè)試增加 @Test 注解
  2. 新建 ByteBuf 并用字節(jié)填充它
  3. 新增 EmbeddedChannel 并添加 FixedLengthFrameDecoder 用于測(cè)試
  4. 寫數(shù)據(jù)到 EmbeddedChannel
  5. 標(biāo)記 channel 已經(jīng)完成
  6. 讀產(chǎn)生的消息并且校驗(yàn)

如上面代碼,testFramesDecoded() 方法想測(cè)試一個(gè) ByteBuf,這個(gè)ByteBuf 包含9個(gè)可讀字節(jié),被解碼成包含了3個(gè)可讀字節(jié)的 ByteBuf。你可能注意到,它寫入9字節(jié)到通道是通過(guò)調(diào)用 writeInbound() 方法,之后再執(zhí)行 finish() 來(lái)將 EmbeddedChannel 標(biāo)記為已完成,最后調(diào)用readInbound() 方法來(lái)獲取 EmbeddedChannel 中的數(shù)據(jù),直到?jīng)]有可讀字節(jié)。testFramesDecoded2() 方法采取同樣的方式,但有一個(gè)區(qū)別就是入站ByteBuf分兩步寫的,當(dāng)調(diào)用 writeInbound(input.readBytes(2)) 后返回 false 時(shí),F(xiàn)ixedLengthFrameDecoder 值會(huì)產(chǎn)生輸出,至少有3個(gè)字節(jié)是可讀,testFramesDecoded2() 測(cè)試的工作相當(dāng)于testFramesDecoded()。

Testing outbound messages

測(cè)試的處理出站消息類似于我們剛才看到的一切。這個(gè)例子將使用的實(shí)現(xiàn)MessageToMessageEncoder:AbsIntegerEncoder。

  • 當(dāng)收到 flush() 它將從 ByteBuf 讀取4字節(jié)整數(shù)并給每個(gè)執(zhí)行Math.abs()。
  • 每個(gè)整數(shù)接著寫入 ChannelHandlerPipeline

圖10.3顯示了邏輯。

Figure 10.3 Encoding via AbsIntegerEncoder

示例如下:

Listing 10.3 AbsIntegerEncoder

public class AbsIntegerEncoder extends MessageToMessageEncoder<ByteBuf> {  //1
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> out) throws Exception {
        while (in.readableBytes() >= 4) { //2
            int value = Math.abs(in.readInt());//3
            out.add(value);  //4
        }
    }
}
  1. 繼承 MessageToMessageEncoder 用于編碼消息到另外一種格式
  2. 檢查是否有足夠的字節(jié)用于編碼
  3. 讀取下一個(gè)輸入 ByteBuf 產(chǎn)出的 int 值,并計(jì)算絕對(duì)值
  4. 寫 int 到編碼的消息 List

在前面的示例中,我們將使用 EmbeddedChannel 測(cè)試代碼。清單10.4

Listing 10.4 Test the AbsIntegerEncoder

public class AbsIntegerEncoderTest {

    @Test   //1
    public void testEncoded() {
        ByteBuf buf = Unpooled.buffer();  //2
        for (int i = 1; i < 10; i++) {
            buf.writeInt(i * -1);
        }

        EmbeddedChannel channel = new EmbeddedChannel(new AbsIntegerEncoder());  //3
        Assert.assertTrue(channel.writeOutbound(buf)); //4

        Assert.assertTrue(channel.finish()); //5
        for (int i = 1; i < 10; i++) {
            Assert.assertEquals(i, channel.readOutbound());  //6
        }
        Assert.assertNull(channel.readOutbound());
    }
}
  1. 用 @Test 標(biāo)記
  2. 新建 ByteBuf 并寫入負(fù)整數(shù)
  3. 新建 EmbeddedChannel 并安裝 AbsIntegerEncoder 來(lái)測(cè)試
  4. 寫 ByteBuf 并預(yù)測(cè) readOutbound() 產(chǎn)生的數(shù)據(jù)
  5. 標(biāo)記 channel 已經(jīng)完成
  6. 讀取產(chǎn)生到的消息,檢查負(fù)值已經(jīng)編碼為絕對(duì)值


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)