一、先上代码
package com.taro.testdemo;
import com.taro.testdemo.demos.service.UserService;
import com.taro.testdemo.demos.web.UserController;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
@SpringBootTest
class TestDemoApplicationTests {
/**
* 1. 测试 UserService
*/
@Autowired
private UserService userService;
@Test
void userServiceTest() {
String message = userService.hello("admin");
System.out.println(message);
}
/**
* 2. 测试单个 Controller: userController
*/
private MockMvc mockMvc;
// @BeforeEach
public void userControllerBefore() {
mockMvc = MockMvcBuilders.standaloneSetup(UserController.class).build();
}
@Test
void userControllerTest() throws Exception {
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders
.get("/user/hello").param("name", "admin");
mockMvc.perform(requestBuilder)
.andDo(MockMvcResultHandlers.print());
}
/**
* 3. 测试一次性注入多个 Controller
*/
@Autowired
private WebApplicationContext context;
@BeforeEach
public void allControllerBefore() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
@Test
void allControllerTest() throws Exception {
// 测试 UserController
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders
.get("/user/hello").param("name", "user");
mockMvc.perform(requestBuilder)
.andDo(MockMvcResultHandlers.print());
System.out.println("=====================================================================");
// 测试 BasicController
MockHttpServletRequestBuilder requestBuilder2 = MockMvcRequestBuilders
.get("/basic/hello").param("name", "basic");
mockMvc.perform(requestBuilder2)
.andDo(MockMvcResultHandlers.print());
}
}
注意:为了单元测试的干净,第二块代码的 @BeforeEach
和第三块代码的 @BeforeEach
最好不要同时存在
二、代码讲解
0. 测试类结构讲解
@SpringBootTest
其作用是启动并加载
Spring
容器上下文点进这个注解会发现其上还有
@BootstrapWith(SpringBootTestContextBootstrapper.class)
,这个注解直译就是和SpringBootTestContextBootstrapper.class
一起启动,而我们一直查看这个类的祖宗类发现在AbstractTestContextBootstrapper
类下有个BootstrapContext
对象,而这个对象就是Spring
容器上下文
org.junit.jupiter.api.Test
和org.junit.Test
区别就是所属版本不同,
org.junit.jupiter.api.Test
是Junit5
版本的,而org.junit.Test
是Junit4
版本的探究它俩就要用到
@RunWith(SpringRunner.class)
这行代码了,它是用来自定义测试运行器的,而且RunWith
和SpringRunner
都是Junit4
的注解,其中SpringRunner
类的父类是SpringJUnit4ClassRunner
,明显可以看出它就是Junit4
的测试运行器自 2.2.x 版本开始,
spring-boot-stater-test
依赖就内置Junit5
为默认的测试运行器
综上,当单元测试类只有
@SpringBootTest
一个注解时,默认使用的是Junit5
的测试运行器,所以Test
类要导入org.junit.jupiter.api
包下的,如果老项目只升级了依赖,要么修改所有的Test
类包路径(不建议),要么给所有测试类加上@RunWith(SpringRunner.class)
代码(建议)
1. 测试 UserService
这个没啥好讲的,需要注意的是,通过 Autowire
注解注入的实际对象是 UserServiceImpl
对象
2. 测试单个 Controller: userController
MockMvc
:用来模拟HTTP请求和Controller
控制器类的MockMvcBuilders.standaloneSetup()
方法:注入单个控制器类用以模拟
3. 测试一次性注入多个 Controller
WebApplicationContext
对象就是 Spring Web
的容器上下文,配合 MockMvcBuilders.webAppContextSetup()
方法就能模拟当前整个项目的所有控制器