springboot企业级项目开发之项目测试——单元测试!

项目测试

项目测试是对项目的需求和功能进行测试,由测试人员写出完整的测试用例,再按照测试用例执行测试。项目测试是项目质量的保证,项目测试质量直接决定了当前项目的交付质量。

测试人员在开展测试之前,首先需要进行测试的需求分析,测试需求分析包括:

测试内容:需要进行哪些方面的测试,包括功能测试、性能测试、可靠性测试、易用性测试和安全性测试等;

测试环境:测试环境的配置;

测试工具:选择测试工具,包括缺陷管理工具和自动化测试工具等;

测试轮数:包括冒烟测试、功能测试、并发测试和弱网测试等。

测试需求分析完成后再开始项目测试。测试的步骤包括:冒烟测试、单元测试、集成测试、性能测试等,最后由产品经理完成产品的验收。

单元测试

项目测试的第一步就是单元测试,单元测试也称为模块测试或组件测试。在项目开发过程中,单元测试用来检查项目的单个单元或模块是否正常工作,它是由开发人员在开发后立即在开发环境中完成的。

为什么要做单元测试

单元测试通常是软件测试中基础的测试类型,用于测试单独模块的功能是否有错。它与功能测试的不同之处是,单元测试更加关注的是代码内部的逻辑,而非功能的完整性。

根据上述描述,在项目中实施单元测试有以下5个目标:

隔离各部分代码的功能;

确保单个模块功能逻辑的正确性;

在开发的过程中及时发现代码缺陷并修改;

发现产品开发早期逻辑中的Bug,以降低测试成本;

允许开发人员后续重构或升级代码。

基于单元测试的目标,在项目中使用单元测试有以下4个优点:

能在产品开发周期的早期发现问题,可以大大地降低测试成本,因为早发现一个缺陷的成本要比晚期发现它的成本低得多。

在改变现有功能(回归测试)的同时,可以减少缺陷。

简化了调试过程(测试驱动开发就是基于测试用例来完成功能开发)。调试是在程序中发现并解决妨碍软件正确运行缺陷的过程。当进行单元测试时,如果发现测试失败,则只需要在调试代码中做一下的更改,就可以快速定位错误。

进行单元测试,能够保证代码质量。

单元测试有哪些内容

单元测试的内容主要包括以下两点:

单元测试的方法:通常使用白盒测试;

单元测试的类型:可以选择手动测试或自动测试。

在对代码进行单元测试时,可以使用手动的方式,也可以使用一些自动化工具。手动测试和自动化测试的区别主要体现在执行效率和操作等方面,如表7.1所示。

在实际开发中流行DevOps(Development+Operations)开发模式,因此建议读者在项目中能使用自动化测试完成的任务尽量采用自动化测试来完成,以提升开发效率。

想要精通单元测试,还需要了解单元测试的几个关键点:

(1)执行单元测试的时间:一般在开发完成后立即进行。

(2)谁做单元测试:开发人员进行自测。

(3)明确单元测试的具体任务,包括两个方面:

首先,准备单元测试的计划,包括:准备测试计划;

回顾测试计划;

修订测试计划;

定义单元测试计划的基准数据。

其次,准备测试用例和脚本,包括:

准备测试环境、测试用例和脚本;

回顾测试用例和脚本;

修订测试用例和脚本。

(4)定义单元测试用例和脚本的基准数据。

(5)执行单元测试,完成后出具单元测试报告。

常规的JUnit测试

JUnit是Java应用开发中使用最广泛的单元测试框架。因为Java 8发布了Lambda表达式,使得Java的编码风格发生了巨大的变化,所以JUnit团队适时推出了新的框架——JUnit 5。JUnit 5能够适应Lambda风格的编码,建议在JDK 1.8及之后版本的项目中使用JUnit 5来创建和执行单元测试。本书中的单元测试以JUnit 5为例。

至于什么是JUnit,看以下官方的定义:

JUnit 5 is the next generation of JUnit. The goal is to create

an up-to-date foundation for developer-side testing on the JVM.

This includes focusing on Java 8 and above, as well as enabling

many different styles of testing.

官方提示JUnit 5是新一代的JUnit,它的目标是为JVM上的开发人员做测试创建一个最新的基础,为Java 8及以上版本创建不同的测试风格。JUnit5框架=JUnit Platform+JUnit Jupiter+JUnit Vintage,其各部分框架的含义如下:

JUnit Platform是在JVM上启动测试框架的基础。

JUnit Jupiter是JUnit 5扩展的新的编程模型和扩展模型,用来编写测试用例。Jupiter子项目为在平台上运行Jupiter的测试提供了一个TestEngine(测试引擎)。

JUnit Vintage提供了一个在平台上运行JUnit 3和JUnit 4的TestEngine。

JUnit 5的架构如图7.1所示。

第一层测试用例:开发人员使用junit-jupiter-api等测试框架的API编写业务代码的单元测试。第二层测试引擎,JUnit测试框架实现引擎API的框架,jupiterengine和vintage- engine分别是JUnit 4和JUnit 5对测试引擎API的实现。

第三层junit-platform-engine:junit-platform-engine平台引擎是对第二层中两种不同引擎实现的抽象,是测试引擎的接口标准。

第四层IDEA:启动器通过ServiceLoader发现测试引擎的实现并安排其执行。它为IDE和构建工具提供了API接口,因此在IDE中可以直接执行测试,例如,启动测试并显示其结果。

在项目中使用JUint 5进行单元测试时需要用到一些注解,如表7.2所示。此外,表中对JUnit 4和JUnit 5的注解使用进行了对比,供读者参考。

根据以上的注解,下面使用JUint 5完成一个单元测试示例。

(1)新建一个项目,在pom.xml中添加JUnit 5的依赖,代码如下:

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>2.3.10.RELEASE</version>

<relativePath/> <!-- lookup parent from repository --></parent>

<groupId>com.example</groupId>

<artifactId>junit5-demo</artifactId>

<version>0.0.1-SNAPSHOT</version>

<name>junit5-demo</name>

<description>Demo project for Spring Boot</description>

<properties>

<java.version>11</java.version>

</properties>

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter</artifactId>

</dependency>

<!--加入JUnit 5的版本测试;如果想用JUnit 4进行测试,把exclusions去除-->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

<optional>true</optional>

</dependency>

</dependencies>

<build>

<plugins>

<plugin>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-maven-plugin</artifactId>

</plugin>

</plugins>

</build>

(2)完成一些简单的业务代码。新建一个用户服务类的接口

UserService:package com.example.junit5demo.service;

import java.util.IllegalFormatException;

/**

* 测试接口

*/

public interface UserService {

/**

* 登录

* @param userName

* @param password

* @return

* @throws IllegalFormatException

*/

boolean login(String userName,String password) throws

IllegalFormat Exception;

/**

* 查询数量

* @return

*/

int countNum();

}

(3)新建一个类来实现UserService接口,这里使用的是最简单的实现方式,暂时先不连接数据库。

package com.example.junit5demo.service;

/**

* 测试接口的实现

*/

import org.springframework.stereotype.Service;@Service

public class UserServiceImpl implements UserService {

@Override

public boolean login(String userName, String password) throws

Illegal

ArgumentException {

if (userName == null || password == null

|| userName.isEmpty() || password.isEmpty()) {

throw new IllegalArgumentException("不能为空");

}

if ("cc".equals(userName) && "123".equals(password)) {

return true;

}

return false;

}

@Override

public int countNum() {

return 18;

}

}

(4)实现UserService接口的方法后,在类名上右击,依次选择Go To |Test | Create New Test...命令,具体的过程如图7.2和图7.3所示。按照上述操作会跳转到选择测试方法的页面,如图7.4所示。选择要测试的方法,选中写的两个方法,单击OK按钮就能自动生成测试类和要测试的方法。

(5)添加Spring Boot的启动类,代码如下:

package com.example.junit5demo;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication

public class Junit5DemoApplication {

public static void main(String[] args) {

SpringApplication.run(Junit5DemoApplication.class, args);

}

}

(6)修改自动生成的测试类代码,完成两个业务方法的单元测试工作。

完成后的代码如下:

package com.example.junit5demo.service;

import lombok.extern.slf4j.Slf4j;

import org.junit.jupiter.api.*;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import static org.junit.jupiter.api.Assertions.*;

@Slf4j

@SpringBootTest //SpringBot测试类注解

class UserServiceImplTest {

//输入要测试的类

@Autowired

private UserService userService;

//在所有测试方法之前执行

@BeforeAll

public static void beforeAll() {

log.info("before all");

}

//在每个测试方法执行之前执行

@BeforeEach

public void beforeEach() {

log.info("before each");

}

@AfterEach

public void afterEach() {

log.info("after each");

}

@AfterAll

public static void afterAll() {

log.info("after all");

} //测试countNum方法

@Test

void countNum() {

int i = userService.countNum();

assertEquals(18, i);

assertNotEquals(1, i);

}

// 测试login方法

@Test

void login() {

boolean cc1 = userService.login("cc", "123");

assertEquals(cc1, true);

boolean cc2 = userService.login("cc2", "123");

assertEquals(cc2, false);

assertThrows(IllegalArgumentException.class, () ->

userService.login("", "123"));

assertThrows(IllegalArgumentException.class, () ->

userService.login("123", null));

}

}

(7)运行这个测试类,在IDEA的控制台打印结果如图7.5所示。可以看到左侧的两个方法都显示绿色的勾,说明已经通过测试用例。至此,我们已经完成了UserService类中所有方法的单元测试,可以看出,相关代码逻辑正确。

提示:在完成单元测试后,如果开发人员在测试过程中优化了代码,对自己的代码进行了重构,则需要对重构后的代码再次进行单元测试,以确保其逻辑的正确性,千万不能忽略这一点。

Mock测试

Mock测试是指在单元测试的过程中对一些不容易构造的对象模拟一个对象进行使用的过程。一些对象只能在特定的环境中产生,例如HttpServletRequest对象必须从Servlet容器中才能构造出来,ResultSet对象必须依赖JDBC的实现才能构造,ProceedingJoinPoint对象的构建必须依赖AOP的实现。在遇到这种复杂对象的构建时,使用一个虚拟的对象(Mock对象)来替代,使用一个“假”的对象,便于在测试时顺利检测复杂对象(虚拟对象)的使用逻辑,以便快速、准确地测试自己的代码逻辑。

Mock的出现是为了解决不同的单元之间由于耦合而难于开发与测试的问题,因此在单元测试和集成测试中都会用到Mock。Mock最大的作用是把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,则可以排除依赖,只验证所调用依赖的行为。例如,需要测试UserService中的方法,但是它依赖UserDao,这时就直接模拟一个用户的数据库对象,且只测试UserService中的方法,来验证USerService中的逻辑正确与否。

在进行单元测试时,以下的几个场景需要用到Mock对象:

需要将当前被测单元和依赖模块分离,构造一个独立的测试环境,不关注被测单元的依赖对象,而只关注被测单元的功能逻辑。例如,被测代码中需要依赖第三方接口的返回值进行逻辑处理,可能因为网络或者其他环境因素,调用第三方平台经常会中断或者失败,而无法对被测单元进行测试,这时就可以使用Mock技术来将被测单元和依赖模块独立开,使得测试可以进行下去。

被测单元依赖的模块尚未开发完成,而被测单元需要依赖模块的返回值进行后续处理。包括前后端分离项目中,后端接口开发完成之前,前端接口需要测试;依赖的上游项目的接口尚未开发完成,需要接口联调测试;service层的代码包含对Dao层的调用,但是Dao层的代码还没完成,需要模拟Dao层的对象。

被测单元依赖的对象较难模拟或者构造比较复杂。例如,HttpServletRequest对象和数据库的连接对象Connection都非常难以构造,则可以直接使用模拟后的对象。

在Java项目开发中有很多Mock框架,常见的有Mockito和PowerMock。

Mockito是一个在项目中最常用的优秀的单元测试Mock框架,它能满足大部分业务的测试要求;PowerMock框架可以解决Mockito框架不能解决的更难的问题,如业务代码中的静态方法、私有方法和Final方法等。PowerMock框架是在EasyMock和Mockito的基础上进行扩展的,它通过提供定制的类进行加载器并进行一些字节码的修改,从而实现更强大的测试功能。

本书使用Java开发中常用的Mockito作为Mock框架。下面介绍如何在Spring Boot项目中使用Mock对象进行测试,从而完成单元测试。

(1)在7.1.3小节中的项目文件pom.xml中添加Mockito依赖,代码如下:

<dependency>

<groupId>org.mockito</groupId>

<artifactId>mockito-core</artifactId>

<version>3.11.2</version>

</dependency>

(2)新建一个产品服务类接口ProductService及其实现类,代码如下:

package com.example.junit5demo.service;

public interface ProductService {

int countNum(); boolean productExists(String name);

}

(3)新建上述ProductService接口的实现类,代码如下:

package com.example.junit5demo.service;

import com.example.junit5demo.dao.ProductDao;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

@Service

public class ProductServiceImpl implements ProductService {

@Autowired

private ProductDao productDao;

@Override

public int countNum() {

return productDao.countNum();

}

@Override

public boolean productExists(String name) {

if (name == null || name.isBlank()) {

return false;

}

return productDao.productExists(name);

}

}

(4)新建一个ProductDao类,直接使用类的实现返回数据,代码如下:

package com.example.junit5demo.dao;

import org.springframework.stereotype.Repository;

import java.util.List;

@Repository

public class ProductDao {

public int countNum() {

return 2;

}

public boolean productExists(String name) {

/**

* 模拟Dao的方法

*/

List<String> apple = List.of("cc","apple", "orgage", "banana");

return apple.contains(name);

}

}

(5)生成ProductService的测试类,代码如下:

package com.example.junit5demo.service;

import com.example.junit5demo.dao.ProductDao;

import org.junit.Assert;

import org.junit.jupiter.api.BeforeEach;

import org.junit.jupiter.api.Test;

import org.junit.jupiter.api.extension.ExtendWith;

import org.mockito.InjectMocks;

import org.mockito.Mock;

import org.mockito.MockitoAnnotations;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.test.context.junit.jupiter.SpringExtension;

import static org.mockito.Mockito.when;// 使用Spring的测试框架

@ExtendWith(SpringExtension.class)

@SpringBootTest

class ProductServiceImplTest {

/**

* 输入要测试的对象

*/

@InjectMocks

ProductServiceImpl productService;

/**

* Mock对象的依赖对象

*/

@Mock

ProductDao productDao;

@BeforeEach

public void setUp() {

/**

* 初始化

*/

MockitoAnnotations.openMocks(this);

}

@Test

void coutNum() {

/**

* 当执行这个方法的时候直接返回5

*/

when(productDao.countNum()).thenReturn(5);

int num = productService.countNum();

/**

* 验证返回值

*/

Assert.assertEquals(num,5);

}

@Test void productExists() {

/**

* 这里本来应该返回true,但是故意设置为false,再查看返回值

*/

when(productDao.productExists("cc")).thenReturn(false);

boolean cc = productService.productExists("cc");

Assert.assertEquals(cc,false);

}

@Test

void productExists3() {

when(productDao.productExists("apple")).thenReturn(false);

boolean cc = productService.productExists("apply");

Assert.assertEquals(cc,false);

}

}

(6)在本次测试用例中,需要测试ProductServiceImpl中的两个业务方法。Product- ServiceImpl依赖ProductDao,这时模拟一个对象到测试对象中,当调用Dao的方法时就会返回设定的值,不会真正地执行Dao,而只测试ProductServiceImpl中的方法。执行本测试类ProductServiceImplTest中的所有测试方法,完成测试后的结果如图7.6所示。可以看到,3个测试用例都已通过,说明已经完成了Mock单元测试。

以上就是使用Mock进行单元测试的过程。Mock测试是单元测试的一大利器,它能帮助开发人员更快地完成单元测试、业务代码的检测和Bug的修复。

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

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

相关文章

Linux kdump远程转存储配置手册教程

一、前言 kdump是一个Linux内核崩溃转储机制,当系统崩溃时,它可以捕获内核的内存转储信息,帮助分析崩溃原因。将转储文件存储到远程位置,便于集中管理和分析。本教程将详细介绍如何配置kdump将转储文件远程转存储。 二、安装kdump 在大多数Linux发行版中,kdump相关的工…

c++bind和forward完美转化

前言 1. std::bind概述 std::bind是C11引入的功能模板&#xff0c;位于<functional>头文件中&#xff0c;用于将函数、成员函数或函数对象与特定参数绑定&#xff0c;生成一个新的可调用对象。 1.1 基本用法 #include <iostream> #include <functional>v…

【Dify精讲】第14章:部署架构与DevOps实践【知识卡片】

第14章&#xff1a;部署架构与DevOps实践http://www.airinto.com/share/49997bb7 一、Docker 容器化方案&#xff1a;从开发到生产的统一 二、Kubernetes 部署&#xff1a;走向云原生 三、CI/CD 流程设计&#xff1a;自动化的艺术 四、高可用架构&#xff1a;让 AI 服务永不停歇…

el-cascader 设置可以手动输入也可以下拉选择

el-cascader 设置可以手动输入也可以下拉选择 稍微修改一下就可食用 <template slot"stationId" slot-scope""><div style"position: relative;"><!-- 可输入也可显示选项 --><el-input:value"stationNameInput"…

Unity Shader开发-着色器变体(1)-着色器变体概述

有时我们希望一份 Shader 源代码可能满足多种功能&#xff08;如处理法线贴图、自发光、不同光照模式、阴影&#xff0c;支持GPUInstacing等多种功能&#xff09;。所以我们需要能够实现Shader分支的方法。 一.Shader分支实现 主要有三种手段实现Shader分支&#xff1a; 1.静…

ECK 简化:在 GCP GKE Autopilot 上部署 Elasticsearch

作者&#xff1a;来自 Elastic Eduard Martin 学习如何使用 GKE Autopilot 和 ECK 在 GCP 上部署 Elasticsearch 集群。 想要获得 Elastic 认证&#xff1f;了解下一次 Elasticsearch Engineer 培训的时间&#xff01; Elasticsearch 拥有丰富的新功能&#xff0c;可以帮助你为…

测试一个软件的性能有哪些指标?

在测试软件性能时,通常会关注多个维度的指标,以评估系统在不同负载下的表现。以下是关键的性能测试指标分类和详细说明: 📊 核心性能指标分类 1. 响应时间(Response Time) 定义:从发送请求到接收到响应所花费的时间 细分: 平均响应时间:所有请求的平均耗时 *P90/P95…

浅析std::atomic<T>::compare_exchange_weak和std::atomic<T>::compare_exchange_strong

目录 std::atomic ::compare_exchange_weak 和 std::atomic ::compare_exchange_strong 核心原理 函数签名 核心区别 典型用法 1. compare_exchange_weak&#xff08;循环内重试&#xff09; 2. compare_exchange_strong&#xff08;单次尝试&#xff09; 底层机制 总…

举出一个异步接口测试的例子

以下是一个完整的 ​异步接口测试​ 实际案例&#xff0c;包含问题场景、解决方案、代码实现和面试回答技巧&#xff0c;适合在面试中展示技术深度&#xff1a; ​案例背景​ ​业务场景​&#xff1a; 测试一个AI图片生成平台的异步接口&#xff0c;用户提交生成请求后&#…

更新麒麟连不上外网

问题&#xff1a;更新麒麟连不上外网 处理&#xff1a;本地建个下载地址 建立文件夹/root/x86.rpm&#xff0c;子文件夹&#xff1a;Packages、repodata&#xff0c;和在线站点建的一样&#xff1a;Index of /NS/V10/V10SP1.1/os/adv/lic/base/x86_64/&#xff0c;然后就下载…

TensorFlow深度学习实战——使用Hugging Face构建Transformer模型

TensorFlow深度学习实战——使用Hugging Face构建Transformer模型 0. 前言1. 安装 Hugging Face2. 文本生成3. 自动模型选择和自动分词4. 命名实体识别5. 摘要生成6. 模型微调相关链接 0. 前言 除了需要实现特定的自定义结构&#xff0c;或者想要了解 Transformer 工作原理外&…

SAP-ABAP:SAP全模块的架构化解析,涵盖核心功能、行业方案及技术平台

一、核心业务模块&#xff08;Logistics & Operations&#xff09; 模块代号核心功能典型流程关键事务码物料管理MM采购/库存/发票校验采购到付款 (P2P)ME21N&#xff08;采购订单&#xff09;, MI31&#xff08;库存盘点&#xff09;销售与分销SD订单/定价/发货/开票订单…

实时预警!机场机坪井室无线智能液位监测系统助力安全降本

某沿海机场因地处多雨区域&#xff0c;每年雨季均面临排水系统超负荷运行压力。经勘测发现&#xff0c;5个井室因长期遭受地下水渗透侵蚀&#xff0c;井壁出现细微结构性裂缝&#xff0c;导致内部水位异常升高。作为机坪地下管网系统的核心节点&#xff0c;这些井室承担着雨水导…

边云协同 AI 视频分析系统设计方案

目录 一、项目背景与目标 二、系统架构概述 总体架构图 三、ER 图&#xff08;核心数据库设计&#xff09; 实体关系图简述 数据表设计&#xff08;简要&#xff09; 四、模型结构图&#xff08;边缘云端AI推理架构&#xff09; 边缘模型&#xff08;YOLOv5-tiny/PP-YO…

vue3整合element-plus

为项目命名 选择vue 框架 选择TS 启动测试&#xff1a; npm run dev 开始整合 element-plus npm install element-plus --save npm install unplugin-vue-components unplugin vitejs/plugin-vue --save-dev 修改main.ts import { createApp } from vue import ./style.cs…

【AI 测试】测试用例设计:人工智能语言大模型性能测试用例设计

目录 一、性能测试可视化架构图 &#xff08;1&#xff09;测试整体架构图 &#xff08;2&#xff09;测试体系架构图 &#xff08;3&#xff09;测试流程时序图 二、性能测试架构总览 &#xff08;1&#xff09;性能测试功能点 &#xff08;2&#xff09;测试环境要…

Windsurf SWE-1模型评析:软件工程的AI革命

引言 软件开发领域正经历着前所未有的变革&#xff0c;AI辅助编程工具层出不穷&#xff0c;但大多数仅专注于代码生成这一环节。Windsurf公司近期推出的SWE-1系列模型打破了这一局限&#xff0c;首次将AI应用扩展至软件工程的全流程。这一举措不仅反映了行业对AI工具认知的深化…

Qt for OpenHarmony 编译鸿蒙调用的动态库

简介 Qt for Harmony​ 是跨平台开发框架 ​Qt​ 与华为 ​OpenHarmony​ 操作系统的深度集成方案&#xff0c;由 Qt Group 与华为联合推动。其核心目标是为开发者提供一套高效工具链&#xff0c;实现 ​​“一次开发&#xff0c;多端部署”​&#xff0c;加速 OpenHarmony 生…

退休时,按最低基数补缴医疗保险15年大概需要多少钱

在南京退休时&#xff0c;如果医保缴费年限不足&#xff08;男需满25年/女需满20年&#xff09;&#xff0c;需补缴差额年限。若按最低基数一次性补缴15年医保&#xff0c;费用估算如下&#xff08;以2024年政策为例&#xff09;&#xff1a; 一、补缴金额计算公式 总补缴费用…

wireshark过滤显示rtmp协议

wireshark中抓包显示的数据报文中&#xff0c;明明可以看到有 rtmp 协议的报文&#xff0c;但是过滤的时候却显示一条都没有 查看选项中的配置&#xff0c;已经没有 RTMP 这个协议了&#xff0c;已经被 RTMPT 替换了&#xff0c;过滤框中输入 rtmpt 过滤即可