Netty实战(九)

news2024/9/30 5:27:19

单元测试

  • 一、什么是单元测试
  • 二、EmbeddedChannel 概述
  • 三、 使用 EmbeddedChannel 测试 ChannelHandler
    • 3.1 测试入站消息
    • 3.2 测试出站消息

一、什么是单元测试

单元测试的基本思想是:以尽可能小的区块测试代码,并且尽可能地和其他的代码模块以及运行时的依赖(如数据库和网络)相隔离。如果应用程序能通过测试验证每个单元本身都能够正常地工作,那么在出了问题时将可以更加容易地找出根本原因。

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

二、EmbeddedChannel 概述

EmbeddedChannel是一种特殊的Channel实现,它是 Netty 专门为改进针对 ChannelHandler的单元测试而提供的。

我们前头说过,可以将 ChannelPipeline 中的 ChannelHandler 实现链接在一起,以构建应用程序的业务逻辑。

这种设计支持将任何潜在的复杂处理过程分解为小的可重用的组件,每个组件都将处理一个明确定义的任务或者步骤。

Netty 提供了它的 Embedded 传输,用于测试 ChannelHandler。这个传输是一种特殊的Channel 实现,这个实现提供了通过 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 的方法,数据是如何流经 ChannelPipeline 的。我们可以使用 writeOutbound()方法将消息写到 Channel 中,并通过 ChannelPipeline 沿着出站的方向传递。随后,可以使用 readOutbound()方法来读取已被处理过的消息,以确定结果是否和预期一样。类似地,对于入站数据,需要使用writeInbound()和readInbound()方法。

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

三、 使用 EmbeddedChannel 测试 ChannelHandler

3.1 测试入站消息

我们先写一个简单的 ByteToMessageDecoder 实现,再对它进行测试。

下图展示了一个简单的 ByteToMessageDecoder 实现。给定足够的数据,这个实现将产生固定大小的帧。如果没有足够的数据可供读取,它将等待下一个数据块的到来,并将再次检查是否能够产生一个新的帧。
在这里插入图片描述
我们可以用代码实现这一过程:

public class FixedLengthFrameDecoder extends ByteToMessageDecoder {//扩展 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;
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in,List<Object> out) throws Exception {
//检查是否有足够的字节可以被读取,以生成下一个帧
while (in.readableBytes() >= frameLength) {
//从 ByteBuf 中读取一个新帧
ByteBuf buf = in.readBytes(frameLength);
//将该帧添加到已被解码的消息列表中
out.add(buf);
}
}
}

ok,下来我们对它进行测试。再此之前,先了解一下JUnit 断言。

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

好了,我们开始写测试代码:

    public class FixedLengthFrameDecoderTest {
        //使用了注解@Test 标注,因此JUnit 将会执行该方法
        @Test
        public void testFramesDecoded() {//第一个测试方法:testFramesDecoded()
           //创建一个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));
            // 将数据写入EmbeddedChannel
            assertTrue(channel.writeInbound(input.retain()));
            //标记 Channel为已完成状态
            assertTrue(channel.finish());
            // 读取所生成的消息,并且验证是否有 3 帧(切片),其中每帧(切片)都为 3 字节
            ByteBuf read = (ByteBuf) channel.readInbound();
            assertEquals(buf.readSlice(3), read);
            read.release();
            read = (ByteBuf) channel.readInbound();
            assertEquals(buf.readSlice(3), read);
            read.release();
            read = (ByteBuf) channel.readInbound();
            assertEquals(buf.readSlice(3), read);
            read.release();
            assertNull(channel.readInbound());
            buf.release();
        }

        @Test
        public void testFramesDecoded2() {//第二个测试方法: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));
           //返回 false,因为没有一个完整的可供读取的帧
            assertFalse(channel.writeInbound(input.readBytes(2)));
            assertTrue(channel.writeInbound(input.readBytes(7)));
            assertTrue(channel.finish());
            ByteBuf read = (ByteBuf) channel.readInbound();
            assertEquals(buf.readSlice(3), read);
            read.release();
            read = (ByteBuf) channel.readInbound();
            assertEquals(buf.readSlice(3), read);
            read.release();
            read = (ByteBuf) channel.readInbound();
            assertEquals(buf.readSlice(3), read);
            read.release();
            assertNull(channel.readInbound());
            buf.release();
        }
    }

这个代码做了什么呢?

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

testFramesDecoded2()方法入站 ByteBuf 是通过两个步骤写入的,当 writeInbound(input.readBytes(2))被调用时,返回了 false。

3.2 测试出站消息

测试出站消息的处理过程和测试入站的类似。我们会使用EmbeddedChannel 来测试一个编码器形式的 ChannelOutboundHandler。

编码器是一种将一种消息格式转换为另一种的组件,下一篇我们会重点去说它,很有意思可以很多事~

出站的基本逻辑大概会是这样:

  • 持有 AbsIntegerEncoder 的 EmbeddedChannel 将会以 4 字节的负整数的形式写出
    站数据;
  • 编码器将从传入的 ByteBuf 中读取每个负整数,并将会调用 Math.abs()方法来获取
    其绝对值;
  • 编码器将会把每个负整数的绝对值写到 ChannelPipeline 中。
public class AbsIntegerEncoder extends MessageToMessageEncoder<ByteBuf> {//扩展 MessageToMessageEncoder 以将一个消息编码为另外一种格式
@Override
protected void encode(ChannelHandlerContext channelHandlerContext,ByteBuf in, List<Object> out) throws Exception {
//检查是否有足够的字节用来编码
while (in.readableBytes() >= 4) {
//从输入的 ByteBuf中读取下一个整数,并且计算其绝对值
int value = Math.abs(in.readInt());
//将该整数写入到编码消息的 List 中
out.add(value);
}
}
}

它的测试代码如下:

public class AbsIntegerEncoderTest {
@Test
public void testEncoded() {
//创建一个 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));
//将该Channel标记为已完成状态
assertTrue(channel.finish());
// 读取所产生的消息,并断言它们包含了对应的绝对值
for (int i = 1; i < 10; i++) {
assertEquals(i, channel.readOutbound());
}
assertNull(channel.readOutbound());
}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/585841.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java: IO流

1.定义 IO流:存储和读取数据的解决方案 用于读写文件中的数据&#xff08;可以读写文件&#xff0c;或网络中的数据...) 2.IO流的分类 1.按着流的方向 1.输入流&#xff1a;读取 2.输出流&#xff1a;写出 2.按照操作文件类型 1.字节流&#xff1a;所有类型文件 体系&…

Redis:缓存击穿、缓存穿透与缓存雪崩的区别、解决方案

0、前言 近期学习redis相关原理&#xff0c;记录一下开发过程中Redis的一些常见问题及应对方法。 1、缓存穿透 一句话总结&#xff1a;先查redis发现没数据&#xff0c;再去数据库查发现还是没数据。 这种情况下缓存永远不会生效&#xff0c;数据库将承担巨大压力。 我们知道&…

前端食堂技术周刊第 84 期:第 96 届 TC39 会议、Deno 五周年、JavaScript 安全最佳实践、2023 Node.js 性能现状

By Midjournery 美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;葡萄冰萃美式 食堂技术周刊仓库地址&#xff1a;https://github.com/Geekhyt/weekly 本期摘要 第 96 届 TC39 会议Deno 五周年JavaScript 安全最佳…

FreeRTOS:信号量

目录 一、信号量是什么二、二值信号量2.1二值信号量简介2.2创建二值信号量2.2.1函数 vSemaphoreCreateBinary()2.2.2函数xSemaphoreCreateBinary()2.2.3 函数 xSemephroeCreateBinaryStatic()2.2.4二值信号量创建过程分析 2.3释放信号量2.3.1函数 xSemaphoreGive ()2.3.2函数 x…

【MySQL学习6:多行输入函数——聚合函数及SQL书写和执行规则】

之前做的笔记都在有道云&#xff0c;之后会一点点将以前的笔记分享出来~ &#xff08;配图在笔记中查看&#xff09; MySQL学习6&#xff1a;多行输入函数——聚合函数及SQL书写和执行规则 SQL书写顺序&#xff1a;SQL99执行顺序&#xff1a;一、常见的聚合函数1. 常见的聚合函…

算法当中的时间、空间复杂度?

1.究竟什么是时间复杂度 时间复杂度是一个函数&#xff0c;它定性描述该算法的运行时间 时间复杂度就是用来方便开发者估算出程序运行的答题时间。 通常会估算算法的操作单元数量来代表程序消耗的时间&#xff0c;这里默认CPU的每个单元运行消耗的时间都是相同的。 假设算法的…

微服务架构之服务监控与追踪

与单体应用相比&#xff0c;在微服务架构下&#xff0c;一次用户调用会因为服务化拆分后&#xff0c;变成多个不同服务之间的相互调用&#xff0c;每个服务可能是由不同的团队开发&#xff0c;使用了不同的编程语言&#xff0c;还有可能部署在不同的机器上&#xff0c;分布在不…

【MySQL】MySQL间隙锁--幻读解决原理

文章目录 一、间隙锁概念二、测试间隙锁范围加锁三、测试等值间隙锁 一、间隙锁概念 当我们用范围条件而不是相等条件检索数据&#xff0c; 并请求共享或排他锁时&#xff0c;InnoDB 会给符合条件的已有数据记录的索引项加锁&#xff1b;对于键值在条件范围内但并不存在的记录…

八、视图集ModelViewSet(重点)

上一章&#xff1a; 七、Django DRF框架GenericAPIView--搜索&排序&分页&返回值_做测试的喵酱的博客-CSDN博客 下一章&#xff1a; 九、DRF生成API文档_做测试的喵酱的博客-CSDN博客 一、视图集ModelViewSet与ReadOnlyViesSet ModelViewSet视图集 与 ReadOnly…

第13届蓝桥杯Scratch国赛真题集锦

编程题 第 1 题 问答题 LED屏幕 题目说明 编程实现 LED屏幕 具体要求: 1).点击绿旗,在舞台中心区域出现由10 x 10方格组成的LED屏幕; 2).按下空格键,LED屏幕最外环方格全部点亮 (方格变为黄色) 3).LED屏幕每秒向内点亮一层,其它LED灯熄灭; 4).直到LED灯在最中心点亮2秒…

games101作业5

作业要求 • Renderer.cpp 中的 Render()&#xff1a;这里你需要为每个像素生成一条对应的光 线&#xff0c;然后调用函数 castRay() 来得到颜色&#xff0c;最后将颜色存储在帧缓冲区的相 应像素中。 • Triangle.hpp 中的 rayTriangleIntersect(): v0, v1, v2 是三角形的三个…

字节跳动测开岗面试居然这么简单....

因为读者里有不少刚入门测试的同学&#xff0c;这两天抽空整理了一份字节测开实习的面试题答案&#xff0c;说实话这个题目真挺简单的&#xff0c;如果你面大厂碰到此类面试题&#xff0c;也算是运气很好啦。大家也可以先自测一下&#xff0c;看看自己能不能答上来。 如果觉得…

vscode 插件 remote-ssh的安装及使用

文章目录 vscode 插件 remote-ssh的安装及使用windows VSCode(clangdremote-ssh) linux(clangd server) 开发环境问题问题1 : getPlatformForHost was canceled问题2 : host 主机不能联互联网问题3 : 每次都要输入密码 vscode 插件 remote-ssh的安装及使用 需要用到的东西1. r…

安装nodejs环境搭建vue项目的框架

说明&#xff1a;想要搭建一个vue项目的框架&#xff0c;先要安装nodejs环境&#xff1b;我的电脑已经安装过&#xff0c;先卸载掉&#xff0c;重新装一遍&#xff08;卸载nodejs参考&#xff1a;http://t.csdn.cn/jHmCU&#xff09; 一、安装nodejs环境 第一步&#xff1a;下…

ssh无密码链接

ssh的基本语法为 ssh host 然后输入密码才可以 如果a想要免密登录b&#xff0c;则a需要将自己的公钥放在b上&#xff0c;原理如下图&#xff1a; 例如&#xff0c;以aricoder这个用户登录的情况下&#xff0c;在服务器01上生成密钥&#xff0c;命令为 ssh-keygen -t rsa然后连…

前端面试题汇总大全二(含答案超详细,Vue,TypeScript,React,Webpack 汇总篇)-- 持续更新

前端面试题汇总大全&#xff08;含答案超详细&#xff0c;HTML,JS,CSS汇总篇&#xff09;-- 持续更新 前端面试题汇总二 五、Vue 篇1. 谈谈你对MVVM开发模式的理解&#xff1f;2. v-if 和 v-show 有什么区别&#xff1f;3. r o u t e 和 route和 route和router区别4.vue自定义…

千万级oracle数据表定时同步到mysql表的方案

1&#xff1a;业务背景 第三方系统提供了一张oracle视图&#xff0c;该视图有六千多万条数据&#xff0c;后续每月会产生三百万条数据的数据。需要每天将数据定时同步到自己系统的mysql表中。 &#xff08;注&#xff1a;我们系统与外界系统访问都要经过一个中间系统的跳转才…

看我0~1精通自动化测试实战,暴涨18K...

前言 开头&#xff0c;先说一下现主流的接口自动化测试框架配套&#xff0c;也是很多企业在使用的。 接口自动化测试&#xff1a;PythonrequestspytestyamlalluerJenkins&#xff1b; Python自动化测试&#xff1a;7天练完这60个实战项目&#xff0c;年薪过35w。 除开测试脚本…

基于秒杀-----分布式锁----lua脚本

基于商品显示秒杀-一人一单业务_xzm_的博客-CSDN博客改进 分布式锁&#xff1a;满足分布式系统或集群模式下多进程可见并且互斥的锁 分布式锁的五个基本要求&#xff1a;多进程可见&#xff0c;互斥&#xff0c;高可用&#xff0c;高性能&#xff0c;安全性 三种实现方式 redi…

《斯坦福数据挖掘教程·第三版》读书笔记(英文版) Chapter 9 Recommendation Systems

来源&#xff1a;《斯坦福数据挖掘教程第三版》对应的公开英文书和PPT Chapter 9 Recommendation SystemsRecommendation systems use a number of different technologies. We can classify these systems into two broad groups. Content-based systems examine properties…