Netty如何測試 ChannelHandler

2018-08-08 10:55 更新

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

測試入站消息

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

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

Figure%2010

Figure 10.2 Decoding via FixedLengthFrameDecoder

實現(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 用來處理入站的字節(jié)并將他們解碼為消息
  2. 指定產(chǎn)出的幀的長度
  3. 檢查是否有足夠的字節(jié)用于讀到下個幀
  4. 從 ByteBuf 讀取新幀
  5. 添加幀到解碼好的消息 List

下面是單元測試的例子,使用 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. 測試增加 @Test 注解
  2. 新建 ByteBuf 并用字節(jié)填充它
  3. 新增 EmbeddedChannel 并添加 FixedLengthFrameDecoder 用于測試
  4. 寫數(shù)據(jù)到 EmbeddedChannel
  5. 標(biāo)記 channel 已經(jīng)完成
  6. 讀產(chǎn)生的消息并且校驗

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

Testing outbound messages

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

  • 當(dāng)收到 flush() 它將從 ByteBuf 讀取4字節(jié)整數(shù)并給每個執(zhí)行Math.abs()。
  • 每個整數(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. 讀取下一個輸入 ByteBuf 產(chǎn)出的 int 值,并計算絕對值
  4. 寫 int 到編碼的消息 List

在前面的示例中,我們將使用 EmbeddedChannel 測試代碼。清單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 來測試
  4. 寫 ByteBuf 并預(yù)測 readOutbound() 產(chǎn)生的數(shù)據(jù)
  5. 標(biāo)記 channel 已經(jīng)完成
  6. 讀取產(chǎn)生到的消息,檢查負(fù)值已經(jīng)編碼為絕對值


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號