码迷,mamicode.com
首页 > Web开发 > 详细

Netty in Action (十九) 第九章节 单元测试

时间:2016-04-29 19:05:57      阅读:129      评论:0      收藏:0      [点我收藏+]

标签:

本章内容包括:

1)单元测试

2)EmbeddedChannel的说明

3)使用EmbeddedChannel测试ChannelHandler


对于一个Netty应用来说,ChannelHandler是一个至关重要的元素,所以充分地去测试ChannelHandler应该是你开发过程中必要的组成部分,我们的最佳实践会告诉你测试不仅能保证你的具体实现的正确性,更重要的是,当你突然变更你的代码的时候,它能很容易地去隔离问题,这种类型的测试叫做单元测试


尽管对单元测试大家没有一个完整的定义,但是更多的参与者觉得单元测试是基础的,是必要的,单元测试的理念基本上是这样:测试尽可能小的代码块,尽可能的隔离其他模块或者是运行时的依赖,数据库网络等带来的影响,如果你验证每一个单元模块的它本身的正确性的话,你会发现当出现bug时,你会很容易找到问题的根源


在这个章节,我们将学习一个特殊的Channel的实现-----EmbededChannel,这个类是由Netty提供的,指定用来构建对ChannelHandler的单元测试的基本建设类


因为代码模块或者代码单元将会脱离它正常的运行环境去做测试,或者你需要一个框架或者工具来运行测试它,在我们所有的例子中,我们使用JUnit4作为我们的测试框架,所以我们需要对JUnit4有着一些基本的了解,如果你对JUnit4完全不了解的话,不要慌张,JUnit4是简单且强大的框架,你可以去www.junit.org网站上去稍微了解一下Junit4一些基本的知识,在这个网站里有你想要知道的一切


9.1 Overview of EmbeddedChannel


你已经掌握了ChannelHandler的一些具体实现可以链式的放入ChannelPipeline中用来构建你应用的业务逻辑模块的实现,我们之前也解释过,这种设计可以把一些潜在的复杂的业务逻辑处理分解成迷你的可重复利用的组件,分解后的Handler都是一个明确的任务或者步骤,在这个章节中,我们也会教你如何简化测试


Netty提供了一个名为嵌入式的传输服务用来测试ChannelHandler,这个传输服务是特殊Channel实现EmbededChannel的一个特性,它可以提供一个简单的方式来让所有的事件通过管道


这个想法是直接有效的,你写入输入输出数据到EmbededChannel中,然后检测是否有内容能够正确地传输到ChannelPipeline的末端,通过这个方法你可以确定消息是否被正确的编码或者解码,是否每一个ChannelHandler定义的动作都被触发执行了


EmbededChannel的一些相关的方法在表9.1中展示了

技术分享

技术分享

输入数据将被ChannelInboundHandler处理代表着从远程端接收数据,输出数据代表被ChannelOutboundChannel处理,代表发送数据到远程端,在测试过程中你可能使用*Inbound或者*Outbound类似这样的方法去测试或者两种方法都使用,这都依赖于你测试的是哪种ChannelHandler



图9.1展示了使用EmbededChannel的方法的时候,数据如何通过ChannelPipeline的情形,你可以使用writeOutbound方法去写入一个message到Channel中,然后这个信息会以输出方向通过ChannelPipeline,随后你可以用readOutbound方法去读取被处理的信息,来确定读取的结果是否与我们预期的结果一样,简而言之,对于输入数据,你可以使用writeInbound或者readInbound方法


在每一个案例中,信息在通过ChannelPipeline中的时候,都是被相关的Handler处理的,要么是ChannelInboundHandler要么是ChannelOutboundHandler,如果消息还没有被消费,你可以使用readInbound或者readOutbound方法在适当的时候读取从channel中出来的信息


让我们详细地讲解一下这两种场景吧,看看他们是如何被应用到你的测试场景中去的


9.2 Testing ChannelHandlers with EmbeddedChannel


在这个小节中,我们将讲解如何使用EmbededChannel测试一个ChannelHandler

技术分享

TIPS:JUnit assertions

org.junit.Assert类提供了很多静态的方法用来测试,一个失败的断言将会导致一个异常被抛出,并且会终止当前的测试,我们可以使用静态导入的方式来引入这些断言,这将会是一个很高效的方法


9.2.1 Testing inbound messages


图92展示了一个简单的ByteToMessageDecoder实现,给予足够的数据,ByteToMessageDecoder会产生出固定大小的帧数据,如果没有足够数量的数据被读取,它将会等待下一个块中的数据,等下一个块数据到达的时候,再次判断是否可以生成出一个固定大小的帧数据


技术分享

我们可以看出右边的帧数据的图形,这个特制的解码器可以生成出3个字节固定大小的帧数据,也就是说,当一个事件触发的时候需要带来足够的字节数来生产一个帧数据


最后生产出来的帧数据会被传播到ChannelPipeline中的下一个管道中去


这个解码器的具体实现如下面的代码清单所示:

技术分享

现在我们编写一个单元测试类来测试这个解码器是否能够按照我们预期的一样去执行,我们之前就提过很多次,即使是一个很简单的代码块,单元测试可以帮助我们在代码重构的期间阻止一些问题的发生或者在问题发生的时候,可以很轻松地诊断出问题的所在


下面是使用EmbededChannel来测试上面解码器的代码

技术分享

技术分享

方法testFramesDecoded验证了当一个ByteBuf中包含9个可读字节的时候,可以被分成3个全新的ByteBuf,每一个ByteBuf中包含3个字节,注意到一个拥有9个字节的ByteBuf调用了writeInboud方法,然后finish方法被执行标志着EmbededChannel完成,最后readInbound方法被调用用来从EmbededChannel中精确地读取三个帧数据和一个null值


testFramesDecoded2方法是简单的,与方法一只有唯一的一点不同,在输入ByteBuf的写入的时候分了2个步骤,当writeInbound(input.readBytes(2))方法被调用的时候,返回了false,这是为什么呢?我们在表9.1中陈述过,如果对于后来对应的readInboud方法能够返回数据,那么writeInbound方法将会返回true,但是我们这个案例中的FixedLengthFrameDecoder只会在输入的字节数等于或者超过3个字节的时候才会产出数据,接下来的测试就会与testFramesDecoded一样了


9.2.2 Testing outbound messages


测试输出信息的过程与我们刚刚写的输入信息测试是很相似的,在接下来的一个例子中,我们将向你展示如何使用EmbededChannel去测试一个编码器类型的ChannelOutboundHandler,这个组件可以将一个信息从一个格式转化成另一个格式,我们将会在下面的一个章节详细地讲解编码器和解码器,目前为止,我们只是在我们测试过程中,简单地提及一下,AbsIntegerEncoder,这是Netty的MessageToMessageEncoder的一个具体实现,用来将负值的Integer转化成它的绝对值


这个例子会如下工作:

1)一个EmbededChannel会持有一个AbsIntegerEncoder,会写入四个字节的负值Integer类型的数据

2)解码器将会从每个ByteBuf中读取负值,然后调用Math.abs()方法来获取他们的绝对值

3)解码器会将每个Integer的绝对值写入到ChannelPipeline中


图9.3展示了这个逻辑

技术分享

下面的代码清单实现了图9.3的逻辑,encode方法将生产的值放入到List中

技术分享

下面的代码清单使用EmbededChannel测试了上面的代码块

技术分享

在这段代码中有一下几个比较重要的步骤:

①写入四字节的负值Integer类型的变量到ByteBuf中

②创建一个EmbededChannel并且分配一个AbsIntegerEncoder给EmbededChannel

③在EmbededChannel中调用writeOutChannel方法写入ByteBuf

④标记channel已经完成

⑤从EmbededChannel的输出端读取所有的Integer类型,然后验证输出数据是不是全部是绝对值


9.3 Testing exception handling


在应用中除了要处理传输数据之外,还会有一些其他的任务,例如,你有时候需要处理畸形数据或者超大数据,在下面的一个例子中,如果读取的字节的数量超过了我们固定的一个限制时,我们会抛出一个TooLongFrameException的异常,这种方式经常使用来防止资源短缺


在图9.4中,我们规定最大的帧数据的大小不能超过三个字节,如果某个帧数据的大小超过了这个限制的话,它的字节数将被消除且会将TooLongFrameException的异常抛出,

在管道中的其他ChannelHandler可以去处理这个异常也可以直接忽视这个异常


技术分享

下面的代码给出了具体的实现

技术分享

同样,我们再次使用EmbededChannel测试一下上面的代码

技术分享

如果对上面的代码只是轻轻一瞥的话,你会发现这个代码与9.2的代码清单看起来很类似,但只有一个小小的不同,其实就是对TooLongFrameException的处理不同,在这里我们用try/catch块包裹住这个代码块其实是EmbededChannel的一个特性,如果其中一个write方法抛出了一个异常的话,它将会以运行时异常的形式抛出,这让测试将变得很简单,因为你可以选择是否在处理数据的过程中处理异常


在这里测试方法是可以用在任何可以抛出异常的ChannelHandler的实现里的


9.4 Summary


用专业的测试工具JUnit来做单元测试会是一个极佳的方法来保证你的代码的正确性并且会提高你代码的可维护性,在这个章节中我们学些了Netty提供的一些测试工具类来测试你自定义的ChannelHandler


在下一个章节中,我们将聚焦于写出一个真实可用的Netty应用,我们将不会再展示任何测试的代码类,所以我们希望你将这个章节讲解的测试方法牢记心中

Netty in Action (十九) 第九章节 单元测试

标签:

原文地址:http://blog.csdn.net/linuu/article/details/51199970

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!