当前位置: 首页 > news >正文

Read book Netty in action(Chapter X)--Unit Testing

序言

ChannelHandler 是Netty 应用程序的关键元素,所以彻底地测试它们应该是你的开发过程的一个标准部分。最佳实践要求你的测试不仅要能够证明你的实现是正确的,而且还要能够很容易地隔离那些因修改代码而突然出现的问题。这种类型的测试叫作单元测试。
单元测试应该做到应盖尽盖!
虽然单元测试没有统一的定义,但是大多数的从业者都有基本的共识。其基本思想是,以尽可能小的区块测试你的代码,并且尽可能地和其他的代码模块以及运行时的依赖(如数据库和网络)相隔离。如果你的应用程序能通过测试验证每个单元本身都能够正常地工作,那么在出了问题时将可以更加容易地找出根本原因。

EmbeddedChannel 概述

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

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

EmbeddedChannel
writeInbound(Object… msgs) 将入站消息写到EmbeddedChannel 中。如果可以通过readInbound()方法从EmbeddedChannel 中读取数据,则返回true

readInbound() 从EmbeddedChannel 中读取一个入站消息。任何返回的东西都穿越了整个ChannelPipeline。如果没有任何可供读取的,则返回null

writeOutbound(Object… msgs) 将出站消息写到EmbeddedChannel中。如果现在可以通过readOutbound()方法从EmbeddedChannel 中读取到什么东西,则返回true

readOutbound() 从EmbeddedChannel 中读取一个出站消息。任何返回的东西都穿越了整个ChannelPipeline。如果没有任何可供读取的,则返回null

finish() 将EmbeddedChannel 标记为完成,并且如果有可被读取的入站数据或者出站数据,则返回true。这个方法还将会调用EmbeddedChannel上的close()方法

入站数据由ChannelInboundHandler 处理,代表从远程节点读取的数据。出站数据由ChannelOutboundHandler 处理,代表将要写到远程节点的数据。根据你要测试的ChannelHandler,你将使用Inbound()或Outbound()方法对,或者兼而有之。

使用EmbeddedChannel 测试ChannelHandler

JUnit 断言
org.junit.Assert 类提供了很多用于测试的静态方法。失败的断言将导致一个异常被抛出,并将终止当前正在执行中的测试。导入这些断言的最高效的方式是通过一个import static 语句来实现:import static org.junit.Assert.*;一旦这样做了,就可以直接调用Assert 方法了:assertEquals(buf.readSlice(3), read);

测试入站消息

给定一个ByteToMessageDecoder的实现,给定足够的数据,这个实现将
产生固定大小的帧。如果没有足够的数据可供读取,它将等待下一个数据块的到来,并将再次检查是否能够产生一个新的帧。

最终,每个帧都会被传递给ChannelPipeline 中的下一ChannelHandler。

这是该解码器的实现:

public class FixedLengthFrameDecoder extends ByteToMessageDecoder {private final int frameLength;public FixedLengthFrameDecoder(int frameLength){if (frameLength <= 0){throw new IllegalArgumentException("frameLength must be a positive integer: " + frameLength);}this.frameLength = frameLength;}@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {if (in.readableBytes() >= frameLength){ByteBuf byteBuf = in.readBytes(frameLength);out.add(byteBuf);}}
}

现在,让我们创建一个单元测试,以确保这段代码将按照预期执行。正如我们前面所指出的,即使是在简单的代码中,单元测试也能帮助我们防止在将来代码重构时可能会导致的问题,并且能在问题发生时帮助我们诊断它们。

 public void testFramesDecoded(){ByteBuf buffer = Unpooled.buffer();for (int i = 0; i < 99; i++) {buffer.writeByte(i);}ByteBuf duplicate = buffer.copy();FixedLengthFrameDecoder fixedLengthFrameDecoder = new FixedLengthFrameDecoder(3);EmbeddedChannel embeddedChannel = new EmbeddedChannel(fixedLengthFrameDecoder);assertTrue(embeddedChannel.writeInbound(duplicate.retain()));assertTrue(embeddedChannel.finish());ByteBuf read =  embeddedChannel.readInbound();duplicate.readBytes(3);read.release();}

该testFramesDecoded()方法验证了:一个包含99 个可读字节的ByteBuf 被解码为33个ByteBuf,每个都包含了3 字节。需要注意的是,仅通过一次对writeInbound()方法的调用,ByteBuf 是如何被填充了99 个可读字节的。在此之后,通过执行finish()方法,将EmbeddedChannel 标记为了已完成状态。最后,通过调用readInbound()方法,从EmbeddedChannel 中正好读取了33个帧和一个null。

测试出站消息

测试出站消息的处理过程和刚才所看到的类似。在下面的例子中,我们将会展示如何使用EmbeddedChannel 来测试一个编码器形式的ChannelOutboundHandler,编码器是一种将一种消息格式转换为另一种的组件 – AbsIntegerEncoder

持有AbsIntegerEncoder 的EmbeddedChannel 将会以4 字节的负整数的形式写出站数据;
编码器将从传入的ByteBuf 中读取每个负整数,并将会调用Math.abs()方法来获取其绝对值;
编码器将会把每个负整数的绝对值写到ChannelPipeline 中。

@Testpublic void testEnc(){ByteBuf buf = Unpooled.buffer();for (int i = 1; i < 10; i++) {buf.writeInt(i * -1);}EmbeddedChannel embeddedChannel = new EmbeddedChannel(new AbsIntegerEncoder());Assert.assertTrue(embeddedChannel.writeOutbound(buf));Assert.assertTrue(embeddedChannel.finish());for (int i = 1; i < 10; i++) {Assert.assertTrue(embeddedChannel.readOutbound().equals(i));}Assert.assertNull(embeddedChannel.readOutbound());}

将4 字节的负整数写到一个新的ByteBuf 中。
创建一个EmbeddedChannel,并为它分配一个AbsIntegerEncoder。
调用EmbeddedChannel 上的writeOutbound()方法来写入该ByteBuf。
标记该Channel 为已完成状态。
从EmbeddedChannel 的出站端读取所有的整数,并验证是否只产生了绝对值。

测试异常处理

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

之前我们曾写过一个编码器FixedLengthFrameDecoder,如果最大的帧大小已经被设置为3 字节。如果一个帧的大小超出了该限制,那么程序将
会丢弃它的字节,并抛出一个TooLongFrameException。位于ChannelPipeline 中的其他ChannelHandler 可以选择exceptionCaught()方法中处理该异常或者忽略它。

 @Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {int readableBytes = in.readableBytes();if (readableBytes > frameLength) {in.clear();throw new TooLongFrameException();}ByteBuf byteBuf = in.readBytes(readableBytes);out.add(byteBuf);}
@Testpublic void testFramesDecodedMax() {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.assertTrue(channel.writeInbound(input.readBytes(2)));try {channel.writeInbound(input.readBytes(4));Assert.fail();} catch (TooLongFrameException e) {// expected exception}assertTrue(channel.writeInbound(input.readBytes(3)));assertTrue(channel.finish());// Read framesByteBuf read = (ByteBuf) channel.readInbound();assertEquals(buf.readSlice(2), read);read.release();read = (ByteBuf) channel.readInbound();assertEquals(buf.skipBytes(4).readSlice(3), read);read.release();buf.release();}

乍一看,这看起来非常类似于前面代码的测试,但是它有一个有趣的转折点,即对TooLongFrameException的处理。这里使用的try/catch块是EmbeddedChannel的一个特殊功能。如果其中一个write方法产生了一个受检查的Exception,那么它将会被包装在一个RuntimeException中并抛出。这使得可以容易地测试出一个Exception是否在处理数据的
过程中已经被处理了。

结束语

使用JUnit 这样的测试工具来进行单元测试是一种非常行之有效的方式,它能保证你的代码的正确性并提高它的可维护性。为我们学习了如何使用Netty 提供的测试工具来测试自定义的ChannelHandler。

相关文章:

Read book Netty in action(Chapter X)--Unit Testing

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

Appium+Python连接真机、跳过登录页、Unexpected error while obtaining UI hierarchy问题

Appium连接真机 使用数据线连接电脑&#xff0c;然后选择文件传输方式 打开手机设置拉至底部&#xff0c;点击关于手机&#xff0c;连续点击7次版本号打开开发者模式 点击设置中的系统与更新&#xff0c;找到开发者选项----> 打开USB调试即可 在终端中输入adb devices确定…...

ES6模块化

目录 一、什么是 ES6 模块化规范 二、ES6 模块化的基本语法 2.1默认导出 2.1默认导入 2.1 注意事项 2.2按需导出 2.2按需导入 2.2按需导出与按需导入的注意事项 2.3直接导入并执行模块中的代码 一、什么是 ES6 模块化规范 ES6 模块化规范是浏览器端与服务器端通用的…...

201809-3 CCF 元素选择器 满分题解(超详细注释代码) + 解题思路(超详细)

问题描述 解题思路 根据题意可以知道在查询中可以分为两种情况 第一种是查询一个标签选择器或者id选择器&#xff08;可以称为一级查询&#xff09; 第二种就是存在大于两级的查询&#xff08;可以称为多级查询&#xff09; 显然第一种查询需要存储每一种元素在内容中所有出现…...

证书拓展域(1)

证书拓展定义了数字证书的标准拓展&#xff0c;每个拓展域GB/T 16264.8-200X中定义的一个OID相关。 这些OID都是id-ce的成员&#xff0c;其定义如下&#xff1a; id-ce OBJECT IDENTIFIER :: { joint-iso-ccitt(2) ds(5) 29 }1.证书策略 certificatePolicies 1.1 定义 本…...

浅谈ChatGPT 和 对AI 的思考

新世纪以来&#xff0c;人工智能作为一个非常热门话题&#xff0c;一直收到大众的广泛的关注。从一开始的图像的分类&#xff0c;检测&#xff0c;到人脸的识别&#xff0c;到视频分析分类&#xff0c;到事件的监测&#xff0c;到基于图片的文本生成&#xff0c;到AI自动写小说…...

NCRE计算机等级考试Python真题(十二)

第十二套试题1、以下关于程序设计语言的描述&#xff0c;错误的选项是&#xff1a;A.Python语言是一种脚本编程语言B.汇编语言是直接操作计算机硬件的编程语言C.程序设计语言经历了机器语言、汇编语言、脚本语言三个阶段D.编译和解释的区别是一次性翻译程序还是每次执行时都要翻…...

Java并发类库提供的线程池有哪几种? 分别有什么特点?

第21讲 | Java并发类库提供的线程池有哪几种&#xff1f; 分别有什么特点&#xff1f; 我在专栏第 17 讲中介绍过线程是不能够重复启动的&#xff0c;创建或销毁线程存在一定的开销&#xff0c;所以利用线程池技术来提高系统资源利用效率&#xff0c;并简化线程管理&#xff0c…...

企业微信如何群发消息到客户群?

为提升工作效率&#xff0c;工作中&#xff0c;企业常常会借助企业微信的群发功能一键发送多个客户。那么企业微信如何群发消息呢&#xff1f; 其中成员个人支持群发消息到客户群&#xff0c;企业也可以创建内容提醒成员进行执行群发。 管理员支持在管理端或在手机端创建企业…...

【信号与系统笔记】第一章 绪论

1.1信号传输系统 信息传输的任务 将带有信息的信号&#xff0c;通过某种系统由发送者传送给接收者。 通信系统的组成 转换器&#xff1a;把消息转换为电信号或者把电信号还原成消息信道&#xff1a;信号传输的通道&#xff0c;广义上来说。发射机和接收机也可以是信道的一部分…...

[神经网络]DETR目标检测网络

一、概述 相较于传统目标检测&#xff0c;DETR是一种纯端到端的网络。它不再需要NMS(非极大值抑制&#xff0c;用于去除多余的预测框)和生成anchor。 DETR提出了一个新的目标函数&#xff08;二分图匹配&#xff09;&#xff0c;这个函数可以强制网络输出一个独一无二的预测值&…...

【服务器管理】connection refused问题解决

简述 在配置服务器的时候&#xff0c;遇到了这个问题。我当时明明已经搭建好了服务&#xff0c;但是我在客户端比如手机上&#xff0c;却怎么都连不上服务器。看日志的话显示的是connection refuesed timeout 这种情况&#xff0c;大概率是服务器的端口没有被打开。 我们只需…...

2023_华为OD机试真题_Python_047_整理扑克牌

整理扑克牌 题目描述 给定一组数字,表示扑克牌的牌面数字,忽略扑克牌的花色,请按如下规则对这一组扑克牌进行整理: 步骤1. 对扑克牌进行分组,形成组合牌,规则如下: 当牌面数字相同张数大于等于4时,组合牌为“炸弹”;3张相同牌面数字 + 2张相同牌面数字,且3张牌与2…...

吐血整理,自动化测试pytest测试框架,资深测试带你少走弯路......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 Pytest框架详解 py…...

SAP BASE64加密及解密

简介&#xff1a;BASE64是一种编码方法&#xff0c;它是一种基于用64个可打印字符来表示二进制数据的表示方法&#xff0c;主要应用于数据存储&#xff0c;传输&#xff0c;打印它是用64个可打印字符表示二进制所有数据方法。由于2的6次方等于64&#xff0c;所以可以用每6个位元…...

【页面无响应】Web页面经常无响应前端如何定位与优化(已解決)

【写在前面】客户现场应用我们的系统时候&#xff0c;发现用着用着就出现1个页面无响应现象&#xff0c;给客户带来极其不好的体验&#xff0c;尤其是当重要工作汇报演示时&#xff0c;就给我看无响应&#xff0c;浏览器崩溃&#xff1f;这样对产品的发展无疑是致命的伤&#x…...

隐私计算 FATE - 多分类神经网络算法测试

​ 一、说明 本文分享基于 Fate 使用 横向联邦 神经网络算法 对 多分类 的数据进行 模型训练&#xff0c;并使用该模型对数据进行 多分类预测。 二分类算法&#xff1a;是指待预测的 label 标签的取值只有两种&#xff1b;直白来讲就是每个实例的可能类别只有两种 (0 或者 1)…...

Codeforces Round 853 (Div. 2)

Codeforces Round 853 (Div. 2) C. Serval and Toxels Arrays 思路&#xff1a; 求任意两个组合的元素个数。 注意到&#xff0c;其实每个元素都是独立的。他在任意组合的出现情况组成的贡献是可以分开讨论的。我们讨论元素x。假设x在m1个数组中出现了cnt次&#xff08;一个…...

Ka频段需要更多带宽?

随着全球连接需求的增长&#xff0c;许多卫星通信(satcom)系统日益采用Ka频段&#xff0c;对数据速率的要求也水涨船高。目前&#xff0c;高性能信号链已经能支持数千兆瞬时带宽&#xff0c;一个系统中可能有成百上千个收发器&#xff0c;超高吞吐量数据速率已经成为现实。 另…...

初学pyinstaller打包过程中的一些问题

记录一下使用pyinstaller打包过程中的一些问题&#xff1a; 不安装虚拟环境打包&#xff0c;直接打包&#xff0c;一般不会出现什么问题&#xff0c;但是打包的exe很大&#xff0c;把所有模块和依赖库也一起打包了。 建议使用虚拟环境打包&#xff0c;安装必要的包&#xff0…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...