当前位置:网站首页>八、开发者工具与单元测试
八、开发者工具与单元测试
2022-08-09 05:12:00 【一只小熊猫呀】
本章概要
- devtools 简介
- devtools 实战
- 单元测试
8.1 devtools 简介
Spring Boot 中提供了一组开发工具 spring-boot-devtools ,可以提高开发者的工作效率,开发者可以将该模块包含在任何项目中,spring-boot-devtools 最方便的地方莫过于热部署了。
8.2 devtools 实战
想要在项目中加入 devtools 模块,只需要添加相关依赖即可
注意:这里多了一个 optional 选项,是为了防止将 devtools 依赖传递到其它模块中。当开发者将应用打包运行后,devtools 会自动被禁用。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
当开发者将 spring-boot-devtools 引入项目后,只要 classpath 路径下的文件发生了变化,项目就会自动重启,这极大的提高了项目的开发速度。
如果开发者使用了 eclipse ,那么在修改完代码并保存后,项目将自动编译并处罚重启,如果使用了 IDEA ,默认情况下,需要开发者手动编译才会触发重启。手动编译时,单击 Build->Build Project 菜单或者按 Ctrl + F9 快捷键进行编译,编译成功后就会触发项目重启。当然,使用 IDEA 也可以配置项目自动编译,步骤如下:
步骤01:单击 File->Settings 菜单,打开 Setting 页面,在左边的菜单栏一次找到 Build,Execution,Deployment->Compile,勾选 Build project automatically,如图:
步骤02:按 Ctrl+Shift+Alt+/ 快捷键调出 Maintenance 页面,如图
单击 Registry ,在新打开的 Registry 页面中,勾选以下复选框
做完这两步配置后,若开发者在此在 IDEA 中修改代码,则项目会自动重启。
注意:classpath 路径下的静态资源或视图模板等发生变化时,并不会导致项目重启。
8.2.2 基本原理
Spring Boot 中使用的自动重启技术涉及两个类加载器,一个是 baseclassloader ,用来加载不会变化的类,例如项目引用的第三方的 jar ;另一个是 restartclassloader,用来加载开发者自己写的会变化的类。
当项目需要重启时,restartclassloader 将被一个新创建的类加载器代替,而 baseclassloader 则继续使用原来的,这种启动方式要比冷启动快得多,因为 baseclassloader 已经存在并且已经加载好了。
8.2.3 自定义监控资源
默认情况下,/META-INF/maven、/META-INF/resources、/resources、/static、/public 以及 /templates位置下资源的变化并不会触发重启,如果想要对这些位置进行重定义,在application.properties中添加如下配置即可:
spring.devtools.restart.exclude=static/**
这表示从默认不触发重启的目录中除去 static 目录,即 classpath:static目录下的资源发生变化时也会导致项目重启。用户也可以反向配置需要监控的目录,配置方式如下:
spring.devtools.restart.additional-paths=src/main/resources/static
这个配置表示当src/main/resources/static 目录下的文件发生变化时,自动重启项目。
由于项目的编码过程是一个连续的过程,并不是每修改一行代码就要重启项目,这样不仅浪费电脑性能,而且没有实际意义。鉴于这种情况,开发者也可以考虑使用触发文件,触发文件是一个特殊的文件,当这个文件发生变化时项目就会重启,配置方法如下:
spring.devtools.restart.trigger-file=.trigger-file
在项目 resources 目录下创建一个名为 .trigger-file 的文件,此时开发者修改代码时,默认情况下项目不会重启,需要项目重启时,开发者只需要修改 .trigger-file 文件即可。但是注意,如果项目没有改变,只是单纯地改变了 .trigger-file 文件,那么项目不会重启。
8.2.4 使用 LiveReload
devtools 默认嵌入了 LiveReload 服务器,可以解决静态文件的热部署,LiveReload 可以在资源发生变化时自动触发浏览器更新,LiveReload 支持 Chrome、Firefox、Safari。以 Chrome 为例,在 Chrome 应用商店搜索 LiveReload ,如图
没搜到,本小结 略
8.2.5 禁用自动重启
如果开发者添加了 spring-boot-devtools 依赖但是不想使用自动重启特性,那么可以关闭自动重启
spring.devtools.restart.enabled=false
8.2.6 全局配置
如果项目模块众多,开发者可以在当前用户目录下创建.spring-boot-devtools 文件来对 devtools 进行全局配置,这个配置文件适用于当前计算机上任何使用了 devtools 模块的 Spring Boot 项目。例:E:\Gitee\chapter04\src\main\resources 目录下创建.spring-boot-devtools.properties 文件,内容如下:
spring.devtools.restart.trigger-file=.trigger-file
此时就实现了使用触发文件触发项目重启。
8.3 单元测试
8.3.1 基本用法
使用 IDEA 创建一个 Spring Boot 项目时,创建成功后,默认都会添加 spring-boot-starter-test 依赖,并且创建好了测试类,代码如下:
@RunWith(SpringRunner.class)
@SpringBootTest
class Test01ApplicationTests {
@Test
void contextLoads() {
}
}
代码解释:
- 首先使用了 @RunWith 注解,该注解将 JUnit 执行类修改为 SpringRunner,而SpringRunner 是 Spring Framework 中测试类 SpringJUnit4ClassRunner 的别名。
- @SpringBootTest 注解除了提供 Spring TestContext 中的常规测试功能之外,还提供了其它特性:提供默认的 ContextLoader、自动搜索 @Spring BootConfiguration、自定义环境属性、为不同的 webEnvionment 模式提供支持,这里的 webEnvironment 模式主要有四种:
(1)MOCK,这种模式是当 classpath 下存在 servletAPIS 时,就会创建 WebApplicationContext 并提供一个 mockservlet 环境;当 classpath 下存在 Spring WebFlux 时,则创建 ReactiveWebApplicationContext;若都不存在,则创建一个常规的 ApplicationContext。
(2)RANDOM_PORT,这种模式将提供一个真实的 Servlet 环境,使用内嵌的容器,但是端口随机。
(3)DEFINED_PORT,这种模式也将提供一个真实的 Servlet 环境,使用内嵌的容器,但是使用定义好的端口。
(4)NONE,这种模式则加载一个普通的 ApplicationContext,不提供任何 Servlet 环境。这种一般不适用于 Web 测试。
- 在 Spring 测试中,开发者一般使用 @ContextConfiguration(class = )或者 @ContextConfiguration(locations = ) 来指定要加载的 Spring 配置,而在 Spring Boot 中则不需要这么麻烦,Spring Boot 中的 @*Test 注解将会去包含测试类的包下查找带有 @SpringBootApplication 或者 @Spring BootConfiguration 注解的主配置类
- @Test 注解则来自 junit,Junit 中的 @After 、 @AfterClass、@Before、@BeforeClass、@Ignore 等注解一样可以在这里使用
8.3.2 Service 测试
Service 层的测试就是常规测试,例如有个 HelloService 如下:
@Service
public class HelloService {
public String sayHello(String name) {
return "Hello " + name + " !";
}
}
需要对 HelloService 进行测试,直接在测试类中注入 HelloService 即可:
@RunWith(SpringRunner.class)
@SpringBootTest
public class Test01ApplicationTests {
@Autowired
HelloService helloService;
@Test
public void contextLoads(){
String hello = helloService.sayHello("唐三");
Assert.assertThat(hello, Matchers.is("Hello 唐三 !"));
}
}
在测试类中注入 HelloService ,然后调用相关方法即可。使用 Assert 判断测试结果是否正确。
8.3.3 Controller 测试
Controller 测试则要使用到 Mock 测试,即对一些不易获取的对象采用虚拟的对象来创建进而方便测试。而 Spring 中提供的 MockMvc 则提供了对 HTTP 请求的模拟,使开发者能够在不依赖网络环境的情况下对 Controller 的快速测试。
@RestController
public class HelloController {
@Autowired
HelloService helloService;
@GetMapping("/hello")
public String hello(String name) {
return helloService.sayHello(name);
}
@PostMapping("/book")
public String addBook(@RequestBody Book book) {
return book.toString();
}
}
public class Book {
private Integer id;
private String name;
private String author;
@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", author='" + author + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
如果要对这个 Controller 进行测试,就需要借助 MockMvc,如下
@RunWith(SpringRunner.class)
@SpringBootTest
public class Test01ApplicationTests {
@Autowired
WebApplicationContext wac;
MockMvc mockMvc;
@Before
public void before() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@Test
public void test1() throws Exception {
MvcResult mvcResult = mockMvc.perform(
MockMvcRequestBuilders
.get("/hello")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("name", "Michael"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print())
.andReturn();
System.out.println(mvcResult.getResponse().getContentAsString());
}
@Test
public void test2() throws Exception {
ObjectMapper om = new ObjectMapper();
Book book = new Book();
book.setAuthor("罗贯中");
book.setName("三国演义");
book.setId(1);
String s = om.writeValueAsString(book);
MvcResult mvcResult = mockMvc
.perform(MockMvcRequestBuilders
.post("/book")
.contentType(MediaType.APPLICATION_JSON)
.content(s))
.andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
System.out.println(mvcResult.getResponse().getContentAsString());
}
}
代码解释:
- 注入一个 WebApplicationContext 用来模拟 ServletContext 环境
- 声明一个 MockMvc 对象,并在每个测试方法执行前进行 MockMvc 的初始化操作(before)
- 调用 MockMvc 中的 perform 方法开启一个 RequestBuilder 请求,具体的请求则通过 MockMvcRequestBuilder 进行构建,调用 MockMvcRequestBuilder 中的 get 方法表示发起一个 GET 请求,调用 post 方法则发起一个 POST 请求,其他的 DELETE 和 PUT 请求也是一样的,最后通过调用 param 方法设置请求参数
- .andExpect(MockMvcResultMatchers.status().isOk()) 表示添加返回值的验证规则,利用 MockMvcRequestBuilder 进行验证,这里表示验证相应码是否为 200
- .andDo(MockMvcResultHandlers.print()) 表示将请求详细信息打印到控制台
- .andReturn() 表示返回相应的 MvcResult ,并使用 mvcResult.getResponse().getContentAsString() 打印出来
- test2 方法演示了 POST 请求如何传递 JSON 数据,首先使用 om.writeValueAsString(book) 将一个 book 对象转换为一段 JSON,然后设置请求类型 .contentType(MediaType.APPLICATION_JSON) ,最后设置请求内容 .content(s)
除了 MockMvc 这种测试方式之外,Spring Boot 还专门提供了 TestRestTemplate 用来实现集成测试,若开发者使用了 @Spring BootTest 注解,则 TestRestTemplate 将自动可用,直接在测试类中注入即可。注意,如果要使用 TestRestTemplate 进行测试,需要将 @Spring BootTest 注解中 webEnvironment 属性的默认值由 WebEnvironment.MOCK 修改为 WebEnvironment.DEFINED_PORT 或者 WebEnvironment.RANDOM_PORT ,因为这两种都是使用一个真实的 Servlet 环境而不是模拟的 Servlet 环境
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Test01ApplicationTests {
@Autowired
TestRestTemplate restTemplate;
@Test
public void test3() {
ResponseEntity<String> hello = restTemplate.getForEntity("/hello?name={0}",String.class,"唐三");
System.out.println(hello);
}
}
8.3.4 JSON 测试
开发者可以使用 @JsonTest 测试 JSON 序列化和反序列化是否工作正常,该注解将自动配置 Jackson ObjectMapper、@JsonComponent 以及 Jackson Modules。如果开发者使用 Gson 代替 Jackson,该注解将配置 Gson。
@RunWith(SpringRunner.class)
@JsonTest
public class JSONTest {
@Autowired
JacksonTester<Book> jacksonTester;
@Test
public void testSerialize() throws IOException {
Book book = new Book();
book.setId(1);
book.setName("三国演义");
book.setAuthor("罗贯中");
Assertions.assertThat(jacksonTester.write(book)).isEqualToJson("book.json");
Assertions.assertThat(jacksonTester.write(book)).hasJsonPathStringValue("@.name");
Assertions.assertThat(jacksonTester.write(book)).extractingJsonPathStringValue("@.name").isEqualTo("三国演义");
}
@Test
public void testDeserialize() throws Exception {
String content = "{\"id\":1,\"name\":\"三国演义\",\"author\":\"罗贯中\"}";
Assertions.assertThat(jacksonTester.parseObject(content).getName())
.isEqualTo("三国演义");
}
}
代码解释:
- 添加 Jackson 注解(@JsonTest),然后注入 JacksonTester 进行 JSON 的序列化和反序列化测试
- Assertions.assertThat(jacksonTester.write(book)).isEqualToJson(“book.json”) 在序列化完成后判断序列化结果是否是所期待的 json,book.json 是一个定义在当前包下的 JSON 文件。
- Assertions.assertThat(jacksonTester.write(book)).hasJsonPathStringValue(“@.name”) 判断对象序列化之后生成的 JSON 中是否有一个名为 name 的 key
- Assertions.assertThat(jacksonTester.write(book)).extractingJsonPathStringValue(“@.name”).isEqualTo(“三国演义”) 判断序列化后 name 对应的值是否为 “三国演义”
- testDeserialize() 方法是反序列化,反序列化完成是判断对象的 name 属性是否为“三国演义”
- book.json 内容如下
{
"id":1,"name":"三国演义","author":"罗贯中"}
边栏推荐
- 不能提取结果集,SQL [n / a]; org.hibernate.exception.SQLGrammarExcept是嵌套的异常
- 【MLT】MLT多媒体框架生产消费架构解析(二)
- 数据库设计---三范式和反范式设计
- Harmony OS ets ArkUI 】 【 】 development create a view and building layout
- 保存Simulink仿真模型为图片或者PDF的方法
- [Daily Training--Tencent Featured 50] 7. Integer Reversal
- STM32系列单片机使用心得
- 【Harmony OS】【ARK UI】轻量级数据存储
- 使用Redis zset做消息队列
- STM32Cube学习笔记(delay)
猜你喜欢
随机推荐
如何让Win11两个屏幕任务栏都显示时间?
什么是通用微处理器、单片机、DSP芯片、嵌入式系统?
剑指Offer-双指针类型题目总结
Harmony OS ets ArkUI 】 【 】 development create a view and building layout
二分搜索篇
进程和计划任务管理
软件测试工程师简历要怎么写,才能让HR看到
Openresty执行lua脚本
查询的结果封装到实体类中并使用集合储存
STM32定时器输入捕获频率(cube)
【Harmony OS】【ArkUI】ets开发 基础页面布局与数据连接
【HMS core】【ML kit】Machine Learning Service FAQ
What is it like to work at Kuaishou?
【HMS Core】【FAQ】【AR Engine】AR Engine FAQ
Faced with risk control, what should Amazon do when evaluating self-supporting accounts?
【ManageEngine】网络性能监控工具
Docker部署MySQL
数字化时代,企业为什么需要商业智能BI
Address book (dynamic version) (C language) (VS)
数组 冒泡排序