首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【SpringBoot(四)】还不懂文件上传?JUnit使用?本文带你了解SpringBoot的文件上传、异常处理、组件注入等知识!并且带你领悟JUnit单元测试的使用!

【SpringBoot(四)】还不懂文件上传?JUnit使用?本文带你了解SpringBoot的文件上传、异常处理、组件注入等知识!并且带你领悟JUnit单元测试的使用!

作者头像
用户11865655
发布2025-10-13 16:44:23
发布2025-10-13 16:44:23
1700
代码可运行
举报
文章被收录于专栏:CSDN专栏CSDN专栏
运行总次数:0
代码可运行

1. 文件上传

1.1 页面表单

代码语言:javascript
代码运行次数:0
运行
复制
<form role="form" action="/upload" method="post" enctype="multipart/form-data">
    <div class="form-group">
        <label for="exampleInputEmail1">邮箱</label>
        <input type="email" class="form-control" name="email"  id="exampleInputEmail1" placeholder="Enter email">
    </div>
    <div class="form-group">
        <label for="exampleInputPassword1">名字</label>
        <input type="password" class="form-control" name="username" id="exampleInputPassword1" placeholder="Password">
    </div>
    <div class="form-group">
        <label for="exampleInputFile">壁纸</label>
        <input type="file" id="exampleInputFile" name="wallpaperInputFile">
    </div>
    <div class="form-group">
        <label for="exampleInputFile">批量照片</label>

        <!-- multiple 代表该 file 属性可以支持多文件上传 -->
        <input type="file" name="photos" multiple>
    </div>


    <div class="checkbox">
        <label>
            <input type="checkbox"> Check me out
        </label>
    </div>
    <button type="submit" class="btn btn-primary">提交</button>
</form>

1.2 文件上传代码

代码语言:javascript
代码运行次数:0
运行
复制
@PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String upload(
    @RequestPart("wallpaperInputFile") MultipartFile wallpaperInputFile,
    @RequestPart("photos") MultipartFile[] photos) throws IOException {

    if (!wallpaperInputFile.isEmpty()){
        String originalFilename = wallpaperInputFile.getOriginalFilename();
        wallpaperInputFile.transferTo(new File("F:\\"+originalFilename));
    }
    if ((photos.length > 0)) {
        for (MultipartFile photo : photos) {
            if (!photo.isEmpty()) {
                String originalFilename = photo.getOriginalFilename();
                photo.transferTo(new File("F:\\"+originalFilename));
            }
        }
    }

    return "index";
}

1.3 注意事项

1.3.1 文件上传大小限量

当提示文件大小过大时,可以修改 spring.servlet.max-file-size 和 max-request-file-size 属性来解决这个问题

代码语言:javascript
代码运行次数:0
运行
复制
# -------------- SpringBoot - properties ----------------

# 文件最大大小
spring.servlet.multipart.max-file-size=10MB
# 请求最大大小
spring.servlet.multipart.max-request-size=100MB
1.3.2 content-Type

文件上传需要指定 content-type 值为:multipart/form-data

因此,无论在控制器还是表单都必须设置好对应的类型

代码语言:javascript
代码运行次数:0
运行
复制
/**
*	----------------控制器-----------
*
*  consumes 使用内容类型 = MediaType.MULTIPART_FORM_DATA_VALUE -- "multipart/form-data"
*  MULTIPART_FORM_DATA_VALUE 常量就是 multipart/form-data
*  该属性限定了接收的内容类型
*/
@PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
代码语言:javascript
代码运行次数:0
运行
复制
<!--
	enctype="multipart/form-data"
	
	enctype 属性可以设置 content-type 的值
	这里直接指定为 multipart/form-data**
-->
<form role="form" action="/upload" method="post" enctype="multipart/form-data"></form>

1.4 文件上传(自动配置)原理

文件上传自动配置类 - MultipartAutoConfiguration-MultiaprtProperties

  • 自动配置好了 StandardServletMultipartResolver【文件上传解析器】
  • 原理步骤:
    1. 请求进来使用文件上传解析器(isMultipart)判断并封装(resolveMultipart返回MultipartHttpServletRequest)文件上传请求
    2. 参数解析器来解析请求中的文件内容,封装成 MultipartFile
    3. 将request中文件信息封装为一个Map

2. 异常处理

2.1 错误处理

2.1.1 默认规则
  • 默认情况下,Spring Boot 提供/error处理所有错误的映射
  • 对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息,对于浏览器客户端,响应一个”whiteLabel“错误视图,以HTML格式呈现相同的数据
  • 要对其进行自定义,添加View解析为error
  • 要完全替换默认行为,可以实现ErrorController并注册该类型的Bean定义,或添加ErrorAttributes类型的组件以使用现有机制但替换其内容
  • error/下的4xx,5xx页面会被自动解析
2.1.2 定制错误处理逻辑
  • 自定义错误页面
    • error/4xx.html error/5xx.html;有精确的错误状态码页面就匹配精确,没有就找 4xx.html ;如果都没有就触发空白页
    • @ControllerAdvice+@ExceptionHandler 处理全局异常
    • @ResponseStatus+自定义异常
      • 底层是ResponseStatusExceptionResolver,把responsestatus注解的信息组装成ModelAndView返回;底层调用 .sendError(statusCode, resolverReason);
    • Spring底层的异常,如:参数类型转换异常;DefaultHandlerExceptionResolver 处理框架底层的异常
      • response.sendError(HttpServletResponse.SC_BAD_REQEUST, ex.getMessage)
    • 自定义需要实现 HandlerExceptionResolver 处理异常,可以作为默认的全局异常处理规则
    • ErrorViewResolver 实现自定义处理异常
      • response.sedError;error请求就会转给controller
      • 异常没有任何方法能处理。tomcat底层 response.sendError。error请求转给controller
      • basicErrorController 要去的页面地址是 ErrorViewResolver
2.1.3 异常处理原理
  • ErrorMvcAutoAConfiguraion 自动配置异常处理规则
    • 容器中的组件:类型 DefaultErrorAttributes -> id errorAttributes
      • DefaultErrorAttributes:定义错误页面中可以包含哪些数据
    • 容器中的组件:类型 BasicErrorController -> id basicErrorController
      • 处理默认 /error 路径的请求:页面响应 new ModelAndView(“error”,model)
      • 容器中有组件View -> id是error;(响应默认错误页)
      • 容器中放组件 BeanNameViewResolver(视图解析器);按照返回的视图名作为组件的id去容器中找View对象
    • 容器中的组件:类型 DefaultErrorViewResolver -> id conventionErrorViewResolver
      • 如果发生错误,会以HTTP的状态码作为视图页地址(viewName),找到真正大页面
      • error/stuts.viewName(404、5xx).html

如果想要返回页面;就会找error视图 StaticView。(默认是白页)

2.1.4 异常处理步骤流程
  1. 执行目标方法,目标方法运行期间有任何异常都会被 cath,而且标注当前请求结束;并且用 dispatchExeption
  2. 进入视图解析流程(页面渲染)
  3. mv = processHandlerException 处理 handler 方法异常,处理完成返回 ModelAndView;
    1. 遍历所有 handlerExceptionResolvers,看谁能处理当前异常【处理器异常解析器】
    2. 系统默认的异常解析器;
      1. DefaultErrorAttributes
      2. HandlerExeptionResolverComposite
        1. ExceptionHandlerExeptionResolver
        2. ResponseStatusExeptionResolver
        3. DefaultHanderExeptionResolver
    3. DefaultErrorAttuibute 先来处理异常,把异常信息保存到request域,并且返回null;
    4. 默认没有任何方法能处理异常,所以异常会被抛出
      1. 如果没有任何方法能处理,最终底层就会发生 /error 请求
      2. 解析错误视图;遍历所有的 ErrorViewResolver 看谁能解析
      3. 默认的 DefaultErrorViewResolver 作用是把响应状态码作为错误页的地址,error/500.html
      4. 模板引擎最终响应这个页面 error/500.html

3. 原生组件注入(Servlet、Filter、Listener)

3.1 使用 Servlet API

@ServletComponentScan(“com.renex.admin”):指定原生Servlet组件都放在哪里

@WebServlet(urlPatterns = “/my”):直接响应,没有Spring的拦截器

@WebFilter(urlPatterns = {“/my”}):拦截指定路径下的所有请求

@WebListener:声明的类是一个 监听类

3.2 使用 RegistrationBean

使用 ServletRegistrationBeanFilterRegistrationBeanServletListenerRegistrationBean

代码语言:javascript
代码运行次数:0
运行
复制
@Configuration
public class MyRegistConfig {

    @Bean
    public ServletRegistrationBean myServlet(){
        MyServlet myServlet = new MyServlet();

        return new ServletRegistrationBean(myServlet,"/my");
    }

    @Bean
    public FilterRegistrationBean myFilter(){
        MyFilter myFilter = new MyFilter();
        // 方式一:
//        return new FilterRegistrationBean(myFilter,myServlet());// 拦截myServlet()返回的全部路径
        // 方式二:
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/my01"));
        return filterRegistrationBean;
    }

    @Bean
    public ServletListenerRegistrationBean myListener(){
        MyListener myListener = new MyListener();
        return new ServletListenerRegistrationBean(myListener);
    }

}

3.3 DispatchServlet 注入原理

3.3.1 DispatchServlet 如何注册进容器?
  • 容器中自动配置了 DisptacherServlet 属性并绑定到 WebMvcProperties; 对应的配置文件的配置项spring.mvc
  • 通过 ServletRegistrationBean 把 DisptacherServlet 配置进来

默认映射的是 / 路径

多个servlet都能处理到同一层路径,精确优先原则

4. 嵌入式 Web 容器

4.1 切换嵌入式Servlet容器

默认支持的 webServer

  • Tomcat,Jetty 或 Undertow
  • ServletWebServerApplicationContext 容器启动寻找 ServletWEbServerFactory 并引导创建服务器

切换服务器

代码语言:javascript
代码运行次数:0
运行
复制
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
	
    <!-- 排除默认的 Tomcat 服务器 -->
    <exclusions>
        <groupId>org.springframework.boot</groupId>
        <artifactid>spring-boot-starter-tomcat</artifactid>
    </exclusions>
</dependency>
<dependency>
    <!-- 设置 undertow 服务器场景包 -->
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

原理:

  • SprigBoot 应用启动,发现当前是 Web 应用。web场景包-导入 tomcat
  • web 应用会创建一个 web 版的 ioc 容器(ServletWebServerApplicationContext)
  • 在 ioc 启动时,寻找 servletServerFactory(Servlet 的 web 服务器工厂 -> Servlet 的 web 服务器 )
  • SpringBoot 底层默认有很多的 WebServer 工厂
  • 底层直接会有一个自动配置类:ServletWebServerFactoryAutoConfiguraion
  • ServletWebServerFactoryAutoConfiguraion 导入了 ServletWebServerFactoryConfiguration(配置类)
  • ServletWebServerFactoryConfigration 配置类根据动态判断系统中到底导入了哪个 Web 服务器的包(默认 Tomcat),容器中就有 TomcatServletWebServerFactory
  • TomcatServletWebServerFactory 创建出 Tomcat 服务器并启动;
    • TomcatWebServer 的构造器,它拥有初始化方法initialize
  • 内嵌服务器就是手动把启动服务器的代码调用(tomcat 核心 jar 包存在)

4.2 定制 Servlet 容器

  • 实现 WebServerFactoryCustomizer
  • 修改配置文件 server.xxx
  • 直接自定义 CongfigurableServletWebServerFactory

xxxxxxCustomizer:定制化器,可以该变 xxxxxx 的默认规则

5. 定制化原理

5.1 定制化的常见方式

修改配置文件

xxxxxCustomizer

编写自定义的配置类 xxConfiguratin + @Bean 替换、增加容器中的默认组件;视图解析器

web应用 实现 WebMvcConfigurer 即可定制化 web 功能 + @Bean 给容器中再扩展一些组件

代码语言:javascript
代码运行次数:0
运行
复制
@Configuration
public class AdminWebConfig impements WebMvcConfigurer

@EnableWebMvc + @WebMvcConfigurer —— @Bean 可以全面接管 SpringMVC,所有规则全部自己重写配置;实现定制和扩展功能

原理:

  1. WebMvcAutoConfiguration 默认的 SpringMVC 的自动配置功能类。包括实现,静态资源、欢迎页…功能
  2. 一旦使用 @EnableWebMvc 会 @Import(DelegatingWebMvcConfiguration.class)
  3. DelegationWebMvcConfiguration 的作用:只保证 SpringMVC 最基本的使用
    1. 把所有系统中的 WebMvcConfigurer 拿来。所有功能的定制都是这些 合起来一起生效
    2. 自动配置了一些非常底层的组件。RequestMappingHandlerMapping。这些组件依赖的组件都是从容器中获取
  4. WebMvcAutoConfiguration 里面的配置要能生效必须 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
  5. @EnableWebMvc 导致了 WebMvcAutoConfiguration 没有生效 因为一旦声明了@EnableWebMvc,由于该注解会自动引入DelegatingWebMvcConfiguration.class,它会将 WebMvcAutoConfiguration 删除,所以导致了 WebMvcAutoConfiguration 没有生效

······

5.2 原理分析套路

场景starter - xxxxAutoConfiguration - 导入xxx组件 - 绑定xxxProperties - 绑定配置文件项

6. 数据访问

6.1 SQL

6.1.1 数据源的自动配置
6.1.1.1 导入JDBC 场景
代码语言:javascript
代码运行次数:0
运行
复制
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

没有数据库驱动?官方不知道我们接下来要操作的数据库

数据库版本和驱动版本对应,SpringBoot 默认会有驱动版本仲裁

导入驱动:

代码语言:javascript
代码运行次数:0
运行
复制
 <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>8.0.30</version>
</dependency>
<!--
想要修改版本
1. 直接引入具体版本(maven的就近依赖原则)
2. 重写声明版本(maven的属性的就近优先原则)
-->
 <properties>
     <java.version>11</java.version>
     <mysql.version>8.0.30</mysql.version>
</properties>
6.1.1.2 分析自动配置
6.1.1.2.1 自动配置的类

DataSourceAutoConfiguration:数据源的自动配置

  • 修改数据源相关的配置:spring.datasource
  • 数据库连接池的配置,是自己容器中没有DataSource才自动配置的
  • 底层默认配置好的连接池是:HikariDataSource
代码语言:javascript
代码运行次数:0
运行
复制
@Configuration(
    proxyBeanMethods = false
)
@Conditional({DataSourceAutoConfiguration.PooledDataSourceCondition.class})
@ConditionalOnMissingBean({DataSource.class, XADataSource.class})
@Import({Hikari.class, Tomcat.class, Dbcp2.class, Generic.class, DataSourceJmxConfiguration.class})
protected static class PooledDataSourceConfiguration {
    protected PooledDataSourceConfiguration() {
    }
}

DataSourceTransctionManagerAutoConfiguration:事务管理器的自动配置

JdbcTemplateAutoConfiguration:JdbcTemplate的自动配置,可以对数据库进行crud

  • 可以修改整个配置项 @ConfigurationProperties(prefix = “spring.jdbc”) 来修改jdbcTemplate

JndiDataSourceAutoConfiguration:Jndi的自动配置

XADataSourceAutoConfiguration:分布式事务相关的

6.1.1.3 修改配置项
代码语言:javascript
代码运行次数:0
运行
复制
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test_1?serverTimezone=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
代码语言:javascript
代码运行次数:0
运行
复制
# 超时处理;此处意思:当查询时间超过 3 秒视为查询超时
spring:
  jdbc:
    template:
      query-timeout: 3 #单位秒
6.1.1.4 测试
代码语言:javascript
代码运行次数:0
运行
复制
@Slf4j
@SpringBootTest
class SpringbootWebAdminApplicationTests {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Test
    void contextLoads() {
        Long aLong = jdbcTemplate.queryForObject("select count(*) from person", long.class);
        log.info("记录总数"+aLong);
    }

}
6.1.2 使用 Druid 数据源
6.1.2.1 Druid 官方 github 地址

Github - Druid

整合第三方技术的两种方式

  • 自定义
  • starter
6.1.2.2 自定义方式
6.1.2.2.1 创建数据源
代码语言:javascript
代码运行次数:0
运行
复制
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.11</version>
</dependency>
6.1.3 整合 MyBatis 操作

https://github.com/mybatis

SpringBoot官方的Starter:spring-boot-starter-*

第三方的:*-spring-boot-starter

6.1.3.1 配置模式:
6.1.3.1.1 引入配置文件
代码语言:javascript
代码运行次数:0
运行
复制
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>
在这里插入图片描述
在这里插入图片描述
6.1.3.1.2 配置模式
  • 全局配置文件
  • SqlSessionFactory:会自动配置
  • SqlSession:自动配置了 SqlSessionTemplate 组合了 SqlSession
  • Mapper:只要写的操作myBatis的接口标注了@Mapper就会备自动扫描进来

可以修改配置文件中 mybatis 的配置文件

代码语言:javascript
代码运行次数:0
运行
复制
# 配置 mybatis 规则
mybatis:
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml
  
# Mapper接口 -> 绑定xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.renex.admin.mapper.PersonMapper">

    <select id="getPerson" resultType="com.renex.admin.pojo.Person">
        select * from person where id = #{id}
    </select>

</mapper>

注意事项:

代码语言:javascript
代码运行次数:0
运行
复制
# 配置 mybatis 规则
mybatis:
#  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml
  # 开启驼峰命名转换
  configuration: # 指定全局配置文件中的相关配置项
    map-underscore-to-camel-case: true

可以不写全局配置文件,所有全局配置文件都放在configuration配置项中即可

使用了 properties(yaml) 就不能使用 xml 配置文件,两者选一个

6.1.3.1.3 步骤:
  1. 导入 mybatis 官方 starter
  2. 编写 mapper 接口。必须标注 @Mapper 注解
  3. 编写 sql 映射文件并绑定mapper接口
  4. 在 application.yaml 中指定 Mapper 配置文件的位置,以及指定全局配置文件的信息(建议:直接配置在mybatis.configuration)
6.1.3.2 注解模式:

与原生mybatis使用并无两样

6.1.3.3 混合模式:

在一个服务类中可以使用注解也可以使用配置模式

7. 单元测试

7.1 JUnit5

SpringBoot 2.2.0 版本开始引入 JUnit5 作为单元测试默认库

​ 作为最小的版本 JUnit 框架,JUnit5 与之前的版本的 Junit 框架有很大的不同。由三个不同的子项目的不同模块组成

Junit 5 = JUnit Platform + Junit Jupiter + Junit Vintage

Junit Platform:Junit Platform 是在 JVM 上启动测试框架的基础,不仅支持 Junit 自制的测试引擎,其他测试引擎也都可以接入。

Junit Jupiter:Junit Jupiter 提供了 JUnit5 的新的编程模型,是Junit5新特性的核心。内部 包含了一个测试引擎,用于在Junit Platform 上运行

Junit Vintage:由于 Junit 已经发展多年,为了照顾老的项目,Junit Vintage 提供了兼容Junit4.x,Junit3.x的测试引擎

在这里插入图片描述
在这里插入图片描述

SpringBoot2.4 以上移除了默认对 Vintage 的依赖。如果需要兼容 junit4 需要自行引入(不能使用Junit4的功能)

如果需要继续兼容 Junit4 需要自行引入 vintage

代码语言:javascript
代码运行次数:0
运行
复制
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-test</artifactId>
     <scope>test</scope>
</dependency>

现在版本:

代码语言:javascript
代码运行次数:0
运行
复制
@SpringBootTest
class Boot05WebAdminApplicationTestsO{
    
    @Test
    void contentLoads(){
        
    }
    
}

以前:

@SpringBootTest + @RunWith(SpringTest.class)

SpringBoot 整合 Junit 以后:

  • 编写测试方法:@Test 标注(注意需要使用Junit5版本的)
  • Junit类具有Spring的功能,@Autowired、比如 @Transactional 标注测试方法,测试完成后自动回滚

7.2 JUnit5 常用注解

Junit5 的注解与JUnit4的注解有所变化。详细更新或注解介绍:JUnit 5 User Guide

注解名称:

详情用法:

@Test

表示方法是测试方法。但是与Junit4的@Test不同。它的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供测试

@ParmeterizedTest

表示方法是参数化测试

@RepeatedTest

表示方法可以重复执行

@DispalyName

为测试类或者测试方法设置展示名称

@BeforeEach

表示在每个单元测试之前执行

@AfterEach

表示在每个单元测试之后执行

@BeforeAll

表示在所有单元测试之前执行

@AfterAll

表示在所有单元测试之后执行

@Tag

表示单元测试类别,类似于JUnit4中的@Categories

@Disabled

表示测试类或测试方法不知想,类似于JUnit4中的@Ignore

@Timeout

表示测试方法运行如果超过了指定时间将会返回错误

@ExtendWith

为测试类或测试方法提供扩展类引用。集合了 SpringBoot 可以使用 @SpringBootTest 进行代替,但是会启动 SpringBoot 的容器功能

7.3 断言(assertions)

断言(assertions)是测试方法中的核心部分,用来对测试需要满足的条件进行验证。这些断言方法都是 org.junit.jupiter.api.Assertions 的静态方法。JUnit5 内置的断言可以分成如下几个类别:

检查业务逻辑返回的数据是否合理

所有的测试运行结束以后,会有一个详细的测试报告;

7.3.1 简单断言

用来对单个值进行简单的验证,如:

方法

说明

assertEquals

判断两个对象或两个原始类型是否相等

assertNotEquals

判断两个对象或两个原始类型是否不相等

assertSame

判断两个对象引用是否指向同一个对象

assertNotSame

判断两个对象引用是否指向不同的对象

assertTrue

判断给定的布尔值是否为 true

assertFalse

判断给定的布尔值是否为 false

assertNull

判断给定的对象引用是否为 null

assertNotNull

判断给定的对象引用是否不为 null

7.3.2 组合断言

assertAll 方法接收多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容的提供这些断言

代码语言:javascript
代码运行次数:0
运行
复制
@DisplayName("组合断言")
@Test
void all(){
    /**
     * 所有断言需要全部成功
     */
    assertAll("test",
            ()-> assertTrue(true && false),
            ()-> assertEquals(1,1)
            );
}
7.3.3 异常断言

在 JUnit4 使其,想要测试方法的异常情况时,需要用 @Rule 注解的ExpectedException 变量还是比较麻烦的。而JUnit5提供了一种新的断言方式 Assertions.assertThrows(),配合函数式编程就可以进行使用

代码语言:javascript
代码运行次数:0
运行
复制
@Test
@DisplayName("异常断言")
void testException(){
    // 断言业务逻辑一定出现异常
    assertThrows(ArithmeticException.class, ()->{
        int i = 1/0;
    },"业务逻辑正常运行!!");
}
7.3.4 超时断言

JUnit5 还提供了 Assertions.asserTimeout() 为测试方法设置了超时时间

代码语言:javascript
代码运行次数:0
运行
复制
@Test
@DisplayName("超时断言")
void testTimeoutAssert(){
    /**
     * 运行时间已超出 1000 毫秒
     */
    assertTimeout(Duration.ofMillis(1000),()->{
        Thread.sleep(1100);
    });
}
7.3.5 快速失败

通过 fail 方法直接使得测试失败

代码语言:javascript
代码运行次数:0
运行
复制
@Test
@DisplayName("快速失败")
void testFail(){
    System.out.println("测试开始.......");
    fail("直接宣告测试失败!");
    System.out.println("测试正在运行....");
    System.out.println("测试结束......");
}

7.4 前置条件(assumptions)

JUnit5 中的前置条件(assumptions 【假设】)类似于断言,不同之处在于不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止。前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。

代码语言:javascript
代码运行次数:0
运行
复制
@Test
@DisplayName("测试前置条件")
void testAssumptions(){
    Assumptions.assumeTrue(assumeIsTrue(),"结果不是true");
    System.out.println("11");
}
boolean assumeIsTrue(){
    return false;
}

assumeTrue 和 assumFalse 确保给定的条件为 true 或 false,不满足条件会使得测试执行种植。assumingThat 的参数是表示条件的布尔值和对应的 Executable 接口的实现对象。只有田间满足时,Executable 对象才会被执行;当条件不满足时,测试执行并不会终止

7.5 嵌套测试

JUnit5 可以通过 Java 中的内部类和 @Nested 注解实现嵌套测试,从而可以更好的把相关的测试方法组织在一起。在内部类中可以使用 @BeforEach 和 @AfterEach 注解,而且嵌套的层次没有限制

内层的 Test 可以驱动外层的 Befor(After)Each / All 之类的方法 提前 / 之后 运行

7.6 参数化测试

参数化测试是 JUnit5 很重要的一个新特性,它使得用不同的参数多次运行测试成为了可能,也为我们的单元测试带来许多遍历。

利用 @ValueSrouce 等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不是每新增一个参数就新增一个单元测试。

注解名称

说明

@ValueSource

为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型

@NullSource

表示为参数化测试提供一个null的入参

@EnumSource

表示为参数化测试提供一个枚举入参

@CsvFileSource

表示读取指定CSV文件内容作为参数化测试入参

@MethodSource

表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)

当然如果参数化测试仅仅只能做到指定普通的入参还达不到让我觉得惊艳的地步。真正强大之处在于他可以支持外部的各类入参。如:CSV,YML,JSON 文件甚至方法的返回值也可以作为入参。只需要去实现 ArgumentsProvider 接口,任何外部文件都可以作为它的入参。

代码语言:javascript
代码运行次数:0
运行
复制
@ParameterizedTest
@DisplayName("参数化测试")
@ValueSource(ints = {1,2,3,4,5,6})
void testParameterize(int i){
    System.out.println(i);
}

@ParameterizedTest
@DisplayName("参数化测试")
@MethodSource("stringProvider")// 方法名
void testParameterize(String i){
    System.out.println(i);
}

static Stream<String> stringProvider(){
    return Stream.of("apple","bannana","atguigu");
}

8. ❤️👌SpringBoot 专栏前文回顾

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 文件上传
    • 1.1 页面表单
    • 1.2 文件上传代码
    • 1.3 注意事项
      • 1.3.1 文件上传大小限量
      • 1.3.2 content-Type
    • 1.4 文件上传(自动配置)原理
  • 2. 异常处理
    • 2.1 错误处理
      • 2.1.1 默认规则
      • 2.1.2 定制错误处理逻辑
      • 2.1.3 异常处理原理
      • 2.1.4 异常处理步骤流程
  • 3. 原生组件注入(Servlet、Filter、Listener)
    • 3.1 使用 Servlet API
    • 3.2 使用 RegistrationBean
    • 3.3 DispatchServlet 注入原理
      • 3.3.1 DispatchServlet 如何注册进容器?
  • 4. 嵌入式 Web 容器
    • 4.1 切换嵌入式Servlet容器
    • 4.2 定制 Servlet 容器
  • 5. 定制化原理
    • 5.1 定制化的常见方式
    • 5.2 原理分析套路
  • 6. 数据访问
    • 6.1 SQL
      • 6.1.1 数据源的自动配置
      • 6.1.2 使用 Druid 数据源
      • 6.1.3 整合 MyBatis 操作
  • 7. 单元测试
    • 7.1 JUnit5
    • 7.2 JUnit5 常用注解
    • 7.3 断言(assertions)
      • 7.3.1 简单断言
      • 7.3.2 组合断言
      • 7.3.3 异常断言
      • 7.3.4 超时断言
      • 7.3.5 快速失败
    • 7.4 前置条件(assumptions)
    • 7.5 嵌套测试
    • 7.6 参数化测试
  • 8. ❤️👌SpringBoot 专栏前文回顾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档