本文重点将讲述如何在单元测试中,mock controller的请求,测试controller请求,同时顺带着讲解如何在单测中测试代码日志中输出的内容。
首先,新建spring boot的项目
按照上面几张图的操作,一个简单的spring boot 项目就建立好了
上图中,我建立了一个gradle管理spring boot项目,项目的结构如下图,左侧图示。
这里要额外说一下,有gradlew / gradlew.bat的脚本,mac 或者 linux系统可以在项目的当前目录下,执行 ./gradlew build 来构建整个项目,Windows使用gradlew.bat的脚本或者在Windows的bash下同样执行 ./gradlew build 来构建整个项目
上图中,标红的部分,是一种代码风格规范约束,代码的缩进换行,统一使用 google java format。
比如,不同的人,可能使用不同的操作系统进行编程,哪怕是使用同样的操作系统,但是每个人的idea的配置都是有差异的,再加上每个人的代码风格习惯不一样,就很难在一个项目形成统一的风格,所以在项目中配置了上述的 google java format, 每个人在提交代码前执行 ./gradlew spotlessApply, 这样整个工程就按照统一的编码风格来规范代码了。
我把上面的配置贴在了下面,方便读者 ctrl + c / ctrl + v
```
id "com.diffplug.gradle.spotless" version "3.27.1"
spotless {
java {
target project.fileTree(project.rootDir) {
include '**/*.java'
}
googleJavaFormat()
}
}
```
打开工程中生成DemoApplication这个spring boot main的主入口文件在上面加入exclude, 排除掉spring security
```
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(
exclude = {
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class
})
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
```
创建 DemoConfiguration类
```
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@ComponentScan({"com.example.demo"})
@EnableAutoConfiguration
public class DemoConfiguration implements WebMvcConfigurer {}
```
spring boot 扫描包的默认规则是扫描和DemoApplication同一目录下的所有内容,因为后续,我们会创建子目录放置controller 、service等bean,所以在DemoConfiguration上指定包扫描了 com.example.demo下所有的文件,参考如下图
创建controller文件夹,在文件夹下新建一个DemoController类
```
package com.example.demo.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
@Slf4j
public class DemoController {
@GetMapping("/log")
public String log() {
log.info("test print log");
return "log";
}
}
```
在这个Controller中,新建了一个http get请求的接口/api/log,实现内打印 test print log这句话
截止到这里,我们新建了一个spring boot项目,并且创建了一个DemoController,对外提供了get方法的/api/log接口。
打开DemoController,mac下快捷键使用 cmd + shift + T, 快速的创建单测类 DemoControllerTest,代码如下
```
package com.example.demo.controller;
import static org.junit.jupiter.api.Assertions.*;
import com.example.demo.extensions.CaptureSystemOutput;
import javax.validation.constraints.NotNull;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
@SpringBootTest
@AutoConfigureMockMvc
@CaptureSystemOutput
class DemoControllerTest {
@Autowired private MockMvc mockMvc;
@Test
void log(@NotNull CaptureSystemOutput.OutputCapture outputCapture) throws Exception {
this.mockMvc
.perform(
MockMvcRequestBuilders.get("/api/log").header("key", "value").param("key", "value"))
.andExpect(MockMvcResultMatchers.status().isOk());
assertTrue(outputCapture.toString().contains("test print log"));
}
}
```
我们使用spring boot内置的mockMvc来模拟浏览器请求,指定get方法的/api/log接口,这里为了演示如何模拟header和请求参数的构造,分别写了.header() .param()来模拟header和请求参数的构造
之后,我们校验的是期望接口返回是200,也就是上图中的 andExpect后的 status.isOk()的单测校验
这里,我加入了一个开源的CaptureSystemOutput的类,参考地址https://github.com/sbrannen/junit5-demo/blob/master/src/test/java/extensions/CaptureSystemOutput.java
这个类的主要用法,就是校验控制台日志输出的内容,是否是我们代码中的期望输出。
这个,也就是如何在单测中,来测试业务代码中,log日志是否正常输出打印。