Spring Boot异常处理机制及对Junit的支持
0x01_异常处理机制
错误页面的修改
默认情况,Spring Boot项目错误页面是Whitelable Error Page
。当项目实际上线,如果给用户显示这个页面就不是很友好。当系统出现异常时应该给用户显示更加友好的错误页面。
- 1.设置具体的状态码页面
在templates/下新建error文件夹,在error中新建:状态码.html
的页面。例如当出现500时显示的页面为500.html
- 2.使用x进行模糊匹配
当出现5开头状态码的错误时,显示页面可以命名为5xx.html
当出现50开头状态码的错误时,显示页面可以命名为50x.html
- 3.统一错误显示页面
在templates下新建error.html
。如果项目中不存在具体状态码的页面或没有使用x成功匹配的页面时,显示error.html
作为错误显示页面。
如果没有模版引擎目录,没有模板引擎(模板引擎找不到这个错误页面),默认静态资源文件夹下找;以上都没有错误页面,就是默认来到
SpringBoot
默认的错误提示页面
上面的比较简单,不再做测试。
利用SpringMVC异常
这个之前分析过,可以参考下面的文章:Spring MVC异常。
0x02_对junit的支持
如果是用IDEA的initializer
创建的Spring boot项目,在spring-boot-starter-test
起步依赖中就有junit的支持:

可以看到Spring boot对于junit5的支持。
在测试包的测试类中,@SpringBootTest
替代了spring-test中的@ContextConfiguration
注解,目的是加载ApplicationContex
t,启动spring容器。
使用@SpringBootTest
时并没有像@ContextConfiguration
一样显示指定locations或classes属性,原因在于@SpringBootTest
注解会自动检索程序的配置文件,检索顺序是从当前包开始,逐级向上查找被@SpringBootApplication
或@SpringBootConfiguration
注解的类。

Spring Test与JUnit等其他测试框架结合起来,提供了便捷高效的测试手段。而Spring Boot Test
是在Spring Test之上的再次封装,增加了切片测试,增强了mock能力。
整体上,Spring Boot Test支持的测试种类,大致可以分为如下三类:
- 单元测试:一般面向方法,编写一般业务代码时,测试成本较大。涉及到的注解有
@Test
。 - 切片测试:一般面向难于测试的边界功能,介于单元测试和功能测试之间。涉及到的注解有
@RunWith
@WebMvcTest
等。 - 功能测试:一般面向某个完整的业务功能,同时也可以使用切面测试中的mock能力,推荐使用。涉及到的注解有
@RunWith
@SpringBootTest
等。
功能测试过程中的几个关键要素及支撑方式如下:
- 测试运行环境:通过
@RunWith
和@SpringBootTest
启动spring容器。 - mock能力:Mockito提供了强大mock功能。
- 断言能力:
AssertJ
、Hamcrest
、JsonPath
提供了强大的断言能力。
一旦依赖了spring-boot-starter-test,下面这些类库将被一同依赖进去:
- JUnit:java测试事实上的标准,默认依赖版本是4.12(JUnit5和JUnit4差别比较大,集成方式有不同)。
- Spring Test & Spring Boot Test:Spring的测试支持。
- AssertJ:提供了流式的断言方式。
- Hamcrest:提供了丰富的matcher。
- Mockito:mock框架,可以按类型创建mock对象,可以根据方法参数指定特定的响应,也支持对于mock调用过程的断言。
- JSONassert:为JSON提供了断言功能。
- JsonPath:为JSON提供了XPATH功能。

@RunWith是Junit4提供的注解,将Spring和Junit链接了起来。假如使用Junit5,不再需要使用@ExtendWith注解,@SpringBootTest和其它@*Test默认已经包含了该注解。
单元测试
在test包下的测试类中,提供了测试的方法:
package com.bones;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringbootjunitApplicationTests {
@Test
void contextLoads() {
}
}
功能测试
一般情况下,使用@SpringBootTest
后,Spring将加载所有被管理的bean,基本等同于启动了整个服务,此时便可以开始功能测试。
由于web服务是最常见的服务,且我们对于web服务的测试有一些特殊的期望,所以@SpringBootTest
注解中,给出了webEnvironment
参数指定了web的environment
,该参数的值一共有四个可选值:
- MOCK:此值为默认值,该类型提供一个mock环境,可以和
@AutoConfigureMockMvc
或@AutoConfigureWebTestClient
搭配使用,开启Mock相关的功能。注意此时内嵌的服务(servlet容器)并没有真正启动,也不会监听web服务端口。 - RANDOM_PORT:启动一个真实的web服务,监听一个随机端口。
- DEFINED_PORT:启动一个真实的web服务,监听一个定义好的端口(从
application.properties
读取)。 - NONE:启动一个非web的ApplicationContext,既不提供mock环境,也不提供真实的web服务。
注:如果当前服务的classpath中没有包含web相关的依赖,spring将启动一个非web的ApplicationContext,此时的webEnvironment就没有什么意义了
切片测试
所谓切片测试,官网文档称为 “slice” of your application,实际上是对一些特定组件的称呼。这里的slice并非单独的类(毕竟普通类只需要基于JUnit的单元测试即可),而是介于单元测试和集成测试中间的范围。
slice是指一些在特定环境下才能执行的模块,比如MVC中的Controller、JDBC数据库访问、Redis客户端等,这些模块大多脱离特定环境后不能独立运行,假如spring没有为此提供测试支持,开发者只能启动完整服务对这些模块进行测试,这在一些复杂的系统中非常不方便,所以spring为这些模块提供了测试支持,使开发者有能力单独对这些模块进行测试。
通过@*Test
开启具体模块的测试支持,开启后spring仅加载相关的bean,无关内容不会被加载。
使用@WebMvcTest
用来校验controllers是否正常工作的示例:
随便准备一个最简单的controller处理单元:
package com.bones.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@RequestMapping("/test")
public String test(){
System.out.println("==================testController=================");
return "test";
}
}
测试:
//@SpringBootTest
@WebMvcTest(TestController.class)
class SpringbootjunitApplicationTests {
@Autowired
MockMvc mvc;
@Test
void testCon() {
try {
ResultActions perform = mvc.perform(MockMvcRequestBuilders.get("/test"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
上面的测试没有启动容器,使用@WebMvcTest
和MockMvc
搭配使用,对Controller进行测试(注意:仅仅只是对controller进行简单的测试,如果Controller中依赖用@Autowired
注入的service、dao等则不能这样测试)。