Netty实战九之单元测试

ChannelHandler是Netty应用程序的关键元素,所以彻底地测试他们应该是你的开发过程的一个标准部分。最佳实践要求你的测试不仅要能够证明你的实现是正确的,而且还要能够很容易地隔离那些因修改代码而突然出现的问题。这种类型的测试叫做单元测试。

其基本思想是,以尽可能小的区块测试你的代码,并且尽可能地和其他的代码模块以及运行时的依赖相隔离。

1、EmbeddedChannel概述

你已经知道,可以将ChannelPipeline中的ChannelHandler实现连接在一起,以构建你的应用程序的业务逻辑。之前已经解释过,这种设计支持将任何潜在的复杂处理过程分解为小的可重用的组件,每个组件都将处理一个明确定义的任务或者步骤。

Netty提供了它所谓的Embedded传输,用于测试ChannelHandler。这个传输是一种特殊的Channel实现——EmbeddedChannel——的功能。这是实现提供了通过ChannelPipeline传播事件的简便方法。

这个想法是直截了当的:将入站数据或者出站数据写入到EmbeddedChannel中,然后检查是否有任何东西到达了ChannelPipeline的尾端。以这种方式,你便可以确定消息是否被编码或者被解码过了,以及是否触发了任何的ChannelHandler动作。

下图展示了使用EmbeddedChannel的方法,数据是如何流经ChannelPipeline的。你可以使用writeOutbound()方法将消息写到Channel中,并通过ChannelPipeline沿着出站的方向传递。随后,你可以使用readOutbound()方法来读取已被处理过的消息,已确定结果是否和预期一样。类似地,对于入站数据,你需要使用writeInbound()和readInbound()方法。

在每种情况下,消息都将会传递过ChannelPipeline,并且被相关的ChannelInboundHandler或者ChannelOutboundHandler处理。如果消息没有被消费,那么你可以使用readInbound()或者readOutbound()方法来在处理过了这些消息之后,酌情把它们从Channel中读出来。 

2、使用EmbeddedChannel测试ChannelHandler

JUnit断言

org.junit.Assert 类提供了很多用于测试的静态方法。失败的断言将导致一个异常被抛出,并将终止当前正在执行中的测试。导入这些断言的最高效的方式是通过一个import static语句来实现:

import static org.junit.Assert.*;

一旦这样做了,就可以直接调用Assert方法了:

assertEquals(buf.readSlice(3),read);

3、测试入站消息

下图展示了一个简单的ByteToMessageDecoder实现。给定足够的数据,这个实现将产生固定大小的帧。如果没有足够的数据可供读取,它将等待下一个数据块的到来,并将再次检查是否产生一个新的帧。 可以从图中右侧的帧看到的那样,这个特定的解码器将产生固定为3字节大小的帧。因此,它可能会需要多个事件来提供足够的字节数以产生一个帧。

最终,每个帧都会被传递给ChannelPipeline中的下一个ChannelHandler,该解码器的实现如下代码所示。

//扩展ByteToMessageDecoder以处理入站字节,并将它们解码为消息
public class FixedLengthFrameDecoder extends ByteToMessageDecoder{

    private final int frameLength;

    //指定要生成的帧的长度
    public FixedLengthFrameDecoder(int frameLength) throws IllegalAccessException {
        if (frameLength <= 0){
            throw new IllegalAccessException("frameLength must be a positive integer: " + frameLength);
        }
        this.frameLength = frameLength;
    }

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf,
                          List<Object> list) throws Exception {
        //检查是否有足够的字节可以被读取,以生成下一个帧
        while (byteBuf.readableBytes() >= frameLength){
            //从ByteBuf中读取一个新帧
            ByteBuf buf = byteBuf.readBytes(frameLength);
            //将该帧添加到已被解码的消息列表中
            list.add(buf);
        }
    }
}

以下代码展示了一个使用EmbeddedChannel的对于前面代码的测试

public class FixedLengthFrameDecoderTest {
    @Test
    public void decode() throws Exception {
        //创建一个ByteBuf,并存储9个字节
        ByteBuf buf = Unpooled.buffer();
        for (int i = 0; i < 9; i++){
            buf.writeByte(i);
        }

        ByteBuf input = buf.duplicate();
        //创建一个EmbeddedChannel,并添加一个FixedLengthFrameDecoder,其将以3字节的帧长度被测试
        EmbeddedChannel channel = new EmbeddedChannel(new FixedLengthFrameDecoder(3));
        //write bytes
        //将数据写入EmbeddedChannel
        assertTrue(channel.writeInbound(input.retain()));
        //标记Channel为已完成状态
        assertTrue(channel.finish());

        //read messages
        //读取所生成的消息,并且验证是否有3帧,其中每帧都为3字节
        ByteBuf read = (ByteBuf)channel.readInbound();
        assertEquals(buf.readSlice(3),read);
        read.release();

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

}

4、测试出站消息

简单地提及我们正在测试的处理器——AbsIntegerEncoder,它是netty的MessageToMessageEncoder的一个特殊化的实现,用于将负值整数转换为绝对值。

该示例将会按照下列方式工作:

——持有AbsIntegerEncoder的EmbeddedChannel将会以4字节的负整数的形式写出站数据。

——编码器将从传入的ByteBuf中读取每个负整数,并将会调用Math.abs()方法来获取其绝对值

——编码器将会把每个负整数的绝对值写到ChannelPipeline中。 以下代码实现了这个逻辑。

public class AbsIntegerEncoder extends MessageToMessageEncoder<ByteBuf>{

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf,
                          List<Object> list) throws Exception {
        //检查是否有足够的字节用来编码
        while (byteBuf.readableBytes() >= 4){
            //从输入的ByteBuf中读取下一个整数,并且计算其绝对值
            int value = Math.abs(byteBuf.readInt());
            //将该整数写入到编码消息的List中
            list.add(value);
        }
    }
}

以下代码使用了EmbeddedChannel来测试代码

public class AbsIntegerEncoderTest {
    @Test
    public void encode() throws Exception {
        //创建一个ByteBuf,并且写入9个负整数
        ByteBuf buf = Unpooled.buffer();
        for (int i = 1; i < 10; i++){
            buf.writeInt(i * -1);
        }
        //创建一个EmbeddedChannel,并安装一个要测试的AbsIntegerEncoder
        EmbeddedChannel channel = new EmbeddedChannel(
                new AbsIntegerEncoder());
        //写入ByteBuf,并断言调用readOutbound()方法将会产生数据
        assertTrue(channel.writeOutbound(buf));
        assertTrue(channel.finish());

        //读取所产生的消息,并断言它们包含了对应的绝对值
        //read bytes
        for (int i=1; i < 10; i++){
            assertEquals(i , channel.readOutbound());
        }
        assertNull(channel.readOutbound());
    }

}

5、测试异常处理

应用程序通常需要执行比转换数据更加复杂的任务。例如,你可能需要处理格式不正确的输入或者过量的数据。下一个示例中,如果所读取的字节数超出了特定的限制,我们将会抛出一个TooLongFrameException。这是一种经常用来防范资源被耗尽的方法。

如下图,最大的帧大小已经被设置为3字节,如果一个帧的大小超过了该限制,那么程序将会丢弃它的字节,并抛出一个TooLongFrameException。位于ChannelPipeline中的其它ChannelHandler可以选择在exceptionCaught()方法中处理该异常或者忽略它。 其实现如下代码所示。

public class FrameChunkDecoder extends ByteToMessageDecoder{

    private final int maxFrameSize;

    public FrameChunkDecoder(int maxFrameSize) {
        this.maxFrameSize = maxFrameSize;
    }

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext,
                          ByteBuf in, List<Object> out) throws Exception {
        int readableBytes = in.readableBytes();
        //如果该帧太大,则丢弃它并抛出异常
        if (readableBytes > maxFrameSize){
            //discard the bytes
            in.clear();
            throw new TooLongFrameException();
        }
        //从ByteBuf中读取一个新的帧
        ByteBuf buf = in.readBytes(readableBytes);
        //将该帧添加到解码消息的List中
        out.add(buf);
    }
}

使用的Try/Catch块是EmbeddedChannel的一个特殊功能。如果其中一个write*方法产生了一个受检查的Exception,那么它将会被包装在一个RuntimeException中并抛出,这使得可以容易地测试出一个Exception是否在处理数据的过程中已经被处理了。

原文地址:https://www.cnblogs.com/UncleCatMySelf/p/9190628.html

时间: 2024-11-09 04:16:28

Netty实战九之单元测试的相关文章

Netty 系列五(单元测试).

一.概述和原理 Netty 的单元测试,主要是对业务逻辑的 ChannelHandler 做测试(毕竟对 Bootstrap.EventLoop 这些做测试着实没有多大意义),模拟一次入站数据或者出站数据,查看数据流经 ChannelHandler 变成什么样了,以此达到测试的目的. Netty 的单元测试将Junit4作为测试框架,将 EmbeddedChannel 作为测试通道.基本原理就是:将入站数据或者出站数据写入 EmbeddedChannel 中,然后检查是否有任何东西到达了 Cha

下载-深入浅出Netty源码剖析、Netty实战高性能分布式RPC、NIO+Netty5各种RPC架构实战演练三部曲视频教程

下载-深入浅出Netty源码剖析.Netty实战高性能分布式RPC.NIO+Netty5各种RPC架构实战演练三部曲视频教程 第一部分:入浅出Netty源码剖析 第二部分:Netty实战高性能分布式RPC 第三部分:NIO+Netty5各种RPC架构实战演练

Netty实战高性能分布式RPC

Netty实战高性能分布式RPC 课程观看地址:http://www.xuetuwuyou.com/course/171 课程出自学途无忧网:http://www.xuetuwuyou.com 一.课程用到的软件 netty4.1.6.Final Spring Tool Suite 3.8.2.RELEASE Maven3.1 Spring4 Zookeeper3.4.6 JDK1.8.0_111 二.课程目标 1.快速学习netty的使用 2.自己学会构建高性能服务器 3.熟练使用多线程之间交

Netty实战-对象编解码,Netty对象网络传递

书籍推荐:        实例代码 :http://download.csdn.net/detail/jiangtao_st/7677503 Server端代码 <span style="font-size:12px;">/** * * <p> * Netty Server Simple * </p> * * LineBasedFrameDecoder + 消息中得换行符 * * @author 卓轩 * @创建时间:2014年7月7日 * @ver

Netty实战(上)视频教程

Netty是一个异步事件驱动的网络应用程序框架, 用于快速开发可维护的高性能协议服务器和客户端. 在分享今天的视频教程之前,我们先介绍一些Netty. Netty是一个NIO客户端服务器框架,可以快速轻松地开发协议服务器和客户端等网络应用程序.它极大地简化并简化了TCP和UDP套接字服务器等网络编程. “快速简便”并不意味着最终的应用程序会受到可维护性或性能问题的影响.Netty经过精心设计,具有丰富的协议,如FTP,SMTP,HTTP以及各种二进制和基于文本的传统协议.因此,Netty成功地找

Netty实战入门详解——让你彻底记住什么是Netty(看不懂你来找我)

一.Netty 简介 Netty 是基于 Java NIO 的异步事件驱动的网络应用框架,使用 Netty 可以快速开发网络应用,Netty 提供了高层次的抽象来简化 TCP 和 UDP 服务器的编程,但是你仍然可以使用底层的 API. Netty 的内部实现是很复杂的,但是 Netty 提供了简单易用的API从网络处理代码中解耦业务逻辑.Netty 是完全基于 NIO 实现的,所以整个 Netty 都是异步的. Netty 是最流行的 NIO 框架,它已经得到成百上千的商业.商用项目验证,许多

使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(九)-- 单元测试

本篇将结合这个系列的例子的基础上演示在Asp.Net Core里如何使用XUnit结合Moq进行单元测试,同时对整个项目进行集成测试. 第一部分.XUnit 修改 Project.json 文件内容,增加XUnit相关的nuget包引用,并修改部分配置. 1 { 2 "version": "1.0.0-*", 3 "testRunner": "xunit", // 设置测试工具为xunit 4 5 "buildOpt

一起学Netty(九)之LengthFieldBasedFrameDecoder

之前介绍了Netty天然的几种解析器,也稍微介绍了一下ByteToMessageDecoder类,我们对Netty的解码器还是有了一定的了解~ 今天要介绍的是Netty中一个很重要的解码器,因为相比于其他的普通的解码器,这个解码器用的场景更多,并不是说其他解码器不重要,只是因为我们业务场景所致 在当今比较流行的水平拆分的架构之下,RPC协议很是流行,这样可以使各个项目解耦,使得更加灵活,每个项目之间通过远程调用交互,相互之间定义一个通讯私有协议,然后解析,这样就可以进行数据接口交互 例如我们定义

Netty实战七之EventLoop和线程模型

简单地说,线程模型指定了操作系统.编程语言.框架或者应用程序的上下文中的线程管理的关键方面.Netty的线程模型强大但又易用,并且和Netty的一贯宗旨一样,旨在简化你的应用程序代码,同时最大限度地提高性能和可维护性. 1.线程模型概述 线程模型确定了代码的执行方式,由于我们总是必须规避并发执行可能会带来的副作用,所以理解所采用的并发模型(也有单线程的线程模型)的影响很重要. 因为具有多核心或多个CPU的计算机现在已经司空见惯,大多数的现代应用程序都利用了复杂的多线程处理技术以有效地利用系统资源