SpringBoot 单元测试

Spring Boot 提供了许多注解和工具帮助开发人员测试应用,在其官方文档中也用了大量篇幅介绍单元测试的使用。在谷歌每周的 TGIF (ThanksGod, it's Friday)员工大会中有一项就是 宣布-周单元测试竞赛获胜的工程师。谷歌之所以这么重视单元测试,就是为了保证程序质量,鼓励大家多写测试代码。国内大多数开发人员对单元测试有所忽视,这也是我写本章内容的原因所在。

本章会围绕 Spring Boot 对单元测试的支持、常用单元测试功能的使用实例以及 MockMvc的自动配置机制展开。

Spring Boot 对单元测试的支持

Spring Boot 对单元测试的支持重点在于提供了-系列注解和工具的集成,它们是通过两个项目提 供 的 : 包 含 核 心 功 能 的 spring-boot-test 项 目 和 支 持 自 动 配 置 的
spring-boot-test-autoconfigure.

通常情况下,我们通过 spring-boot-starter-test 的 Starter 来引入 SpringBoot 的核心支持项目以及单元测试库。spring-boot-starter-test 包 含的类库如 JUnit:一个 Java 语言的单元测试框架。

Spring Test & Spring Boot Test:为 Spring Boot 应用提供集成测试和工具支持。

AssertJ:支持流式断言的 Java 测试框架。

.Hamcrest: 一个匹配器库。

Mockito :一个 Java Mock 框架。

JSONassert:一个针对 JSON 的断言库。

JsonPath:一个 JSON XPath 库。

如果 Spring Boot 提供的基础类库无法满足业务需求,我们也可以自行添加依赖。依赖注入的优点之一就是可以轻松使用单元测试。 这种方式可以直接通过 new 来创建对象,而不需要涉及 Spring。当然,也可以通过模拟对象来替换真实依赖。

如果需要集成测试,比如使用 Spring 的 ApplicationContext, Spring 同样能够提供无须部署应 用 程 序 或 连 接 到 其 他 基 础 环 境 的 集 成 测 试 。 而 SpringBoot 应 用 本 身 就 是 一 个ApplicationContext,因此除了正常使用 Spring.上下文进行测试,无须执行其他操作。

常用单元测试注解

以 Junit 为例,在单元测试中会常用到一些注解,比如 Spring Boot 提供的@SpringBootTest

@MockBean、@SpyBean 、@WebMvcTest@AutoConfigureMockMvc 以及 Junit 提供的@RunWith 等。下面以- 一个简单的订单插入的功能示例进行说明。

@RunWith(SpringRunner .class)
public class OrderServiceTest {
@Autowired
private OrderService orderService;
ublic void testInsert() {Order order = new Order()
order . setOrderNo("A001");
order. setUserId(100);
orderService. insert (order);
}
}

我们先来看 Junit 中的@RunWith 注解,该注解用于说明此测试类的运行者,比如示例中使用 的 SpringRunner 。 SpringRunner 是 由 spring-test 提 供 的 , 它 实 际 上 继 承 了SpringJUnit4ClassRunner 类,并且未重新定义任何方法,我们可以将 SpringRunner 理解为 SpringJUnit4ClassRunner 更简洁的名字。

@SpringBootTest 注解由 Spring Boot 提供,该注解为 SpringApplication 创建上下文并支持 Spring Boot 特性。

该测试项目中引入了 spring-boot-starter-test 依赖,默认情况下此依赖使用的单元测试类库为 J∪nit4,此时@SpringBootTest 注解需要配合@RunWith(SpringRunner.class)注解使用,否则注解会被忽略。

查看@SpringBootTest 注解的源码,会发现其内部枚举类 WebEnvironment 提供了支持的多种单元测试模式。

@Target(ElementType. TYPE)
@Retention(Retent ionPolicy . RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper. class)
@ExtendWith(SpringExtension. class)
public @interface SpringBootTest {
@AliasFor("properties")
String[] value() default {};
@AliasFor("value" )
String[] properties() default {};
String[] args() default {};
Class<?>[] classes() default {};
WebEnvi ronment webEnvironment() default WebEnvironment . MOCK;
enum WebEnvironment {
MOCK(false),
RANDOM PORT(true)
DEFINED_ PORT(true),
NONE(false); } }

从@SpringBootTest 的源代码中可以看出,通过 WebEnvironment 枚举类提供了 MOCK、RANDOM_ PORT、DEFINED_ PORT 和 NONE 这 4 种环境配置。

:Mock:加载 WebApplicationContext 并提供 Mock Servlet 环境,嵌入的 Servlet 容器不会被启动。

:RANDOM_ PORT:加载一个
EmbeddedWebApplicationContext 并提供真实的 Servlet 环境。嵌入的 Servlet 容器将被启动,并在一个随机端口上监听。

:DEFINED_ PORT:加载一个
EmbeddedWebApplicationContext 并提供真实的 Servlet 环境。嵌入的 Servlet 容器将被启动,并在一个默认的端口上监听(application.properties 配 置端口或者默认端口 8o8o)。

:NONE:使用 SpringApplication 加载一个 ApplicationContext,但是不提供任何 Servlet 环境。

示例中默认采用此种方式。

关于其他的注解就不再展开了,在后面章节中会结合具体示例进行说明。

JUnit5 单元测试示例

在上节中已经提到 JUnit5 与 JUnit4 有所不同,本节还是用同样的示例来看一下 JUnit5 的使用。

@SpringBootTest
public class OrderServiceTest {
@Resource
private OrderService orderService;
@Test
public void testInsert()
Order order = new Order();
order . setOrderNo( "A001");
order . setUserId(100);
orderService . insert (order);}

通过上面的代码,我们可以看出默认情况下只需要使用@SpringBootTest 注解即可,而在上节@SpringBootTest 源代码中已经看到组合了@ExtendWith(SpringExtension.class)注解,因此此示例无须注解。

这里需要注意的是 Spring Boot 的版本信息,在 2.1.x 之后@SpringBootTest 注解中才组合了@ExtendWith(SpringExtension.class)注解。因此,需要根据具体使用的版本来确定是否需要@ExtendWith(SpringExtension.class)注解,否则可能会出现注解无效的情况虽然单元测试类的代码与 JUnit4 基本相同,但本质上还是有区别的。比如,在使用 JUnit5时, 默认的 spring-boot- starter-test 依赖类库已经无法满足,需要手动引|入 junit-jupiter.

<!-- Junit 5 -->
<dependency><groupId>org . junit. jupiter</ groupId>
<artifactId>junit - jupiter</ artifactId>
<version>5.5.2</version>
<scope>test</scope>
/ dependency>

同时,如果必要则需要将 junit-vintage-engine 进行排除。

<dependency>
<groupId>org. springframework . boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org. junit . vintage</ groupId>
<artifactId>junit -vintage - engine</artifactId>
</exc lusion>
</exclusions>
</dependency>

上面的测试代码还有一个经常会遇到的问题,就是从 JUnit4 升级到 JUnit5 时,如果你只是把类上的注解换了,会发现通过@Resource 或@Autowired 注入的 OrderService 会抛出空指针异常。这是为什么呢?

原因很简单,从 JUnit4 升级到 JUnit5 时,在 testInsert 方法 上的@Test 注解变了。在 JUnit4中默认使用的@Test 注解为 org.junit.Test,而在 JUnit5 中需要使用
org.junit.jupiter.api.Test.因此,如果在升级的过程中出现莫名其妙的空指针异常时,需考虑到此处。

总体来说,JUnit5 的最大变化是 @Test 注解改为由几个不同的模块组成,其中包括 3 个不同子项目: JUnit Platform、JUnit Jupiter 和 JUnit Vintage.同时,JUnit5 也提供了一套自己的注解。

.@ BeforeAll 类似于 JUnit 4 的@BeforeAll,表示使用了该注解的方法应该在当前类中所有使用了@Test、@ RepeatedTest、@ ParameterizedTest 或者@TestFactory 注解的方法之前执行,且必须为 static。

.@ BeforeEach 类似于 JUnit 4 的@Before,表示使用了该注解的方法应该在当前类中所有使用了@Test、@ RepeatedTest、@ ParameterizedTest 或者@TestFactory 注解的方法之前执行。

.@Test 表示该方法是一个测试方法。

.@ DisplayName 为测试类或测试方法声明一个自定义的显示名称。

.@AfterEach 类似于 JUnit 4 的@After,表示使用了该注解的方法应该在当前类中所有使用了@Test、@RepeatedTest 、@ ParameterizedTest 或者@ TestFactory 注解的方法之后执行。

.@AfterAll 类似于 JUnit 4 的@AfterClass, 表示使用了该注解的方法应该在当前类中所有使用了@Test、@RepeatedTest、 @ ParameterizedTest 或者@ TestFactory 注解的方法之后执行,且必须为 static。

.@Disable 用于禁用一个测试类或测试方法,类似于 JUnit 4 的@Ignore.

.@ExtendWith 用于注册自定义扩展功能。

关于这些注解的详细使用,我们就不一一举例了。

Web 应用单元测试

在面向对象的程序设计中,模拟对象(mock object)是以可控的方式模拟真实对象行为的假对象。在编程过程中,通常通过模拟一些输入数据,来验证程序是否达到预期效果。

模拟对象-般应用于真实对象有以下特性的场景:行为不确定、真实环境难搭建、行为难触发、速度很慢、需界面操作、回调机制等。

在上面章节中实现了 Service 层的单元测试示例,而当对 Controller 层进行单元测试时,便需要使用模拟对象,这里采用 spring-test 包中提供的 MockMvc。MockMvc 可以做到不启动项目工程就可以对接口进行测试。

MockMvc 实现了对 HTTP 请求的模拟,能够直接使用网络的形式,转换到 Controller 的调用,这样可以使得测试速度快、不依赖网络环境,同时提供了一套验证的工具, 使得请求的验证统-一而且方便。

下面以一个具体的示例来对 MockMvc 的使用进行讲解。在使用之前,依旧需要先引入对应的依赖。

<dependency>
<groupId>org. springfr amework . boot</ groupId>
<artifactId>spring boot - starter- test</ artifactId>
<scope>test</scope>
</ dependency>
这里创建一一个简单的 TestController,提供一个 hello 方法, 返回一个字符串。
@RestController
public class TestController {
@RequestMapping(" /mock" )
public String mock(String name) {
return "Hello ”+ name + "!"; }}

下面编写单元测试的类和方法,我们这里都采用基于 JUnit4 和 SpringBoot 2.x 版本进行操作。

@RunWith(SpringRunner . class)@SpringBootTest
@AutoConfigureMockMvc
public class TestControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testMock() throws Exception {
// mockMvc. perform 执行一 个请求
mockMvc . perform(MockMvcRequestBuilders
// MockMvcRequestBuilders. get( "XX")构造一个请求. get(" /mock")
//设置返回值类型为 utf-8, 否则默认为 ISO- 8859-1
. accept (MediaType . APPLICATION_ JSON_ _UTF8_ _VALUE)
/ ResultActions . param 添加请求传值
. param( "name", "MockMvc"))
// Resul tActions . andExpect 添加执行完成后的断言
. andExpect (MockMvcResultMatchers . status(). isOk())
. andExpect (MockMvcResultMatchers . content(). string("Hello MockMvc!"))
// Resul tActions . andDo 添加一一个结果处理器,此处打印整个响应结果信息
. andDo(MockMvcResultHandlers . print());
}
}

执行该单元测试打印结果部分内容如下。

MockHttpServletRequest:
HTTP Method
GET
Request URI = /mock
Parameters = {name=[MockMvc]}
Headers = [Accept:" application/json; charset=UTF-8" ]
Body = null
Session Attrs = {}
Handler:
Type = com. secbro2. learn. controller. TestController
Method = public java. lang . String com. secbro2 . learn. controller .Test
Controller .mock(java. lang. String)
MockHttpServletResponse:
Status = 200Error message = nul
Headers = [Content -Type :"application/json;charset=UTF-8", Content-Length:
"14" ]
Content type = application/json;charset-UTF-8
Body = Hello MockMvc!

在以上单元测试中,@RunWith(SpringRunner. class )和@SpringBootTest 的作用我们已经知道,另外的@AutoConfigureMockMvc 注解提供了自动配置 MockMvc 的功能。 因此,只需通过@Autowired 注入 MockMvc 即可。

MockMvc 对象也可以通过接口 MockMvcBuilder 的实现类来获得。该接口提供一个唯一的build 方法来构造 MockMvc。主要有两个实现类:

StandaloneMockMvcBuilder 和 DefaultMockMvcBuilder,分别对应两种测试方式,即独立安装和集成 Web 环境测试(并不会集成真正的 web 环境,而是通过相应的 Mock API 进行模拟测 试 , 无 须 启 动 服 务 器 ) 。 MockMvcBuilders 提 供 了 对 应 的 standaloneSetup 和webAppContextSetup 两种创建方法,在使用时直接调用即可。MockMvc 对象的创建默认使用 DefaultMockMvcBuilder,后面章节会详细介绍这一过程。

整个单元测试包含以下步骤:准备测试环境、执行 MockMvc 请求、 添加验证断言、添加结果处理器、得到 MvcResult 进行自定义断言/进行下一步的异步请求、卸载测试环境。

关于 Web 应用的测试,还有许多其他内容,比如:检测 Web 类型、检测测试配置、排除测试配置以及事务回滚(通过@ Transactional 注解),读者朋友可根据需要自行编写单元测试用例进行尝试。

本文给大家讲解的内容是SpringBoot对单元测试支持、常用单元测试功能使用实例

  1. 下篇文章给大家讲解的是MockMvc的自动配置;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!

SpringBoot对单元测试支持、常用单元测试功能使用实例相关推荐

  1. 常用libcurl功能编程实例

    目录 导入和导出Cookie 在多线程GTK中使用curl 使用SSL上下文回调 CURLOPT_DEBUGFUNCTION如何使用? epoll和timerfd的多套接字API使用 导入和导出Coo ...

  2. springboot系列文章之使用单元测试

    前言 springboot提供了 spirng-boot-starter-test以供开发者使用单元测试,在引入 spring-boot-starter-test依赖后: <dependency ...

  3. Android单元测试一:单元测试入门

    1. 前言 在Android开发中,如果对一个简单的功能,每次修改代码都重新运行到设备中进行测试,会浪费大量时间,降低开发工作效率.如果使用单元测试,编写单元测试类,执行测试单元测试类就可以对某些功能 ...

  4. python语言案例教程单元测试答案_Python单元测试框架(附例子)

    什么是单元测试? 单元测试在[Python中做是为了在应用程序的开发阶段的早期识别错误时的错误不太经常和修复成本更低. 单元测试是用Python设计的脚本化代码级测试,用于验证功能的小"单元 ...

  5. SpringBoot项目中集成第三方登录功能

    SpringBoot项目中集成第三方登录功能 引言 1 环境准备 2 代码实现 3 第三方平台认证申请 4 打包和部署项目 5 第三方平台登录认证测试 6 参考文章 引言 最近想把自己在公众号上介绍过 ...

  6. android单元测试作用,Android单元测试(二):再来谈谈为什么

    今天早上8点半坐到桌子前,打开电脑,看了几分钟体育新闻,做其他一些准备工作,到9点开始真正开始着手写这篇文章.于是开始google,找资料,打算列一大段冠冕堂皇的理由,来说明为什么要写单元测试,比如: ...

  7. 什么是单元测试(UnitTest),单元测试的作用是什么

    单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证.对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类, ...

  8. php gridview,PHP编程:yii2-GridView在开发中常用的功能及技巧总结

    <PHP编程:yii2-GridView在开发中常用的功能及技巧总结>要点: 本文介绍了PHP编程:yii2-GridView在开发中常用的功能及技巧总结,希望对您有用.如果有疑问,可以联 ...

  9. JAVA 正则表达式4种常用的功能

    JAVA 正则表达式4种常用的功能 正则表达式在字符串处理上有着强大的功能,sun在jdk1.4加入了对它的支持 下面简单的说下它的4种常用功能: 查询: 以下是代码片段: String str=&q ...

最新文章

  1. 干货丨零基础学习大数据,搭建Hadoop处理环境
  2. linux安装tune2fs工具,linux tune2fs简解(每日一令之五)
  3. volatile变量与普通变量的区别
  4. VTK:Qt之SideBySideRenderWindowsQt
  5. as3.0-----计时器的使用实例
  6. 阿里云开源EasyTransfer:业界首个面向NLP场景深度迁移学习框架
  7. 视觉SLAM笔记(56) 位姿图优化
  8. Personal Leetcode solution(Python) 1~20
  9. 数据库设计的几个建议
  10. 注册(三)之设置Contact地址的过期参数
  11. 共享文件夹只能连接20人_英语正能量 | 快乐可以与人共享,苦难却只能自己坚强...
  12. 三菱PLC编程口通信上位机端报文和java示范代码
  13. 计算机考试报名照片可以是白底吗,软考报名照片必须白底的是吗?
  14. pandas的认识与dataframe的认识 day04课件代码
  15. golang学习之五:error、painc、recover
  16. 关于VM开启黑屏的解决方法
  17. OceanBase详解(详解OceanBase特性)
  18. C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(二十四) Be careful!前方怪物出没
  19. OPPO VIVO等多平台官方远程真机测试平台
  20. 基于云虚拟机的代码覆盖率

热门文章

  1. 一大早给大家打声招呼
  2. c语言课程设计中小车运动,C语言课程设计_运动的小车动画.doc
  3. 解决es启动no java的问题
  4. OPPOA57T_官方线刷包_救砖包_解账户锁
  5. 戴尔t430服务器性能,不止双路E5 这款塔式服务器有点强
  6. 传球问题(组合数学问题)
  7. 013 仓储物流自动化行业里的“品质感”
  8. 【招人啦】达坦科技寻优秀实习生/23届校招生/社招大佬
  9. python中字典del的用法_python中字典(Dictionary)用法实例详解
  10. 第二章 表格及样式入门 ③ 详细代码演练、解析