0%

SpringBoot Junit Restful Api 测试用例

Controller的单元测试

第一种使用模拟环境进行测试

原理

使用MockMvc发起请求,然后执行API中相应的代码,在执行的过程中使mock模拟底层数据的返回,最后结果验证。
Spring测试框架提供MockMvc对象,可以在不需要客户端-服务端请求的情况下进行MVC测试,完全在服务端这边就可以执行Controller的请求,跟启动了测试服务器一样。
测试开始之前需要建立测试环境,setup方法被@Before修饰。通过MockMvcBuilders工具,使用WebApplicationContext对象作为参数,创建一个MockMvc对象。

MockMvc对象提供一组工具函数用来执行assert判断,都是针对web请求的判断。这组工具的使用方式是函数的链式调用,允许程序员将多个测试用例链接在一起,并进行多个判断。通常我们会用到下面的一些工具函数:
perform(get(...))建立web请求。通过MockMvcRequestBuilder执行GET请求。post、delete分别对应POSTDELETE请求
andExpect(...)可以在perform(...)函数调用后多次调用,表示对多个条件的判断,这个函数的参数类型是ResultMatcher接口,在MockMvcResultMatchers这这个类中提供了很多返回ResultMatcher接口的工具函数。这个函数使得可以检测同一个web请求的多个方面,包括HTTP响应状态码(response status),响应的内容类型(content type),会话中存放的值,检验重定向、model或者header的内容等等。这里需要通过第三方库json-path检测JSON格式的响应数据:检查json数据包含正确的元素类型和对应的值,例如jsonPath("$.name").value("中文测试")用于检查在根目录下有一个名为name的节点,并且该节点对应的值是“中文测试”。

MockMvc对象的引入的两种方式

1.测试类上添加@AutoConfigureMockMvc注解,该注解表示启动测试的时候自动注入 MockMvc,然后直接@Autowired注入MockMvc对象,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void userMapping() throws Exception {
String JSON =
"{\"nickName\":\"岗村一泽\",\"lastName\":\"岗村\",\"firstName\":\"一泽\",\"email\":\"gangcunyiza@gmail.com\",\"password\":\"12345678\",\"jpFirstName\":\"一泽\",\"jpLastName\":\"岗村\",\"address1\":\"大阪\",\"address2\":\"台東区東上野3丁目\",\"address3\":\"19番6号\",\"phone\":\"2014561\",\"postalCode\":\"1100112\"}";
Mockito.when(userService.findByEmailAndStatus("gangcunyiza@gmail.com.com",1)).thenReturn(user);
Mockito.when(userRepository.findByNickNameAndStatus("岗村一泽",1)).thenReturn(user);
mockMvc.perform(MockMvcRequestBuilders.post("/registe")
.accept(MediaType.APPLICATION_JSON).content(JSON)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn();
}
}

2.使用MockMvcBuilder构建MockMvc对象,示例代码如下:、

1
2
3
4
5
6
7
8
9
10
11
12
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest4 {
@Autowired
private WebApplicationContext web;
private MockMvc mockMvc;

@Before
public void setupMockMvc() {
mockMvc = MockMvcBuilders.webAppContextSetup(web).build();
}
}

用法

1.MockMvcBuilders.webAppContextSetup(WebApplicationContext context):指定WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的MockMvc;
2.部分方法说明
perform:执行一个RequestBuilder请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理;
andExpect:添加ResultMatcher验证规则,验证控制器执行完成后结果是否正确;
andDo:添加ResultHandler结果处理器,比如调试时打印结果到控制台;
andReturn:最后返回相应的MvcResult;然后进行自定义验证/进行下一步的异步处理;
3.MockMvc 测试整个流程程说明:
1.mockMvc.perform执行一个请求;
2.MockMvcRequestBuilders.get("/1/2")构造一个请求
3.ResultActions.andExpect添加执行完成后的断言
4.ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情,比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。
5.ResultActions.andReturn表示执行完成后返回相应的结果。
4.传参

  • 路径请求

mockMvc.perform(MockMvcRequestBuilders
.请求方式(“url/{path}”,参数值)

  • 表单请求

mockMvc.perform(MockMvcRequestBuilders
.请求方式(“url”).param(“键”,“值”).contentType(MediaType.APPLICATION_FORM_URLENCODED)

  • JSON请求

mockMvc.perform(MockMvcRequestBuilders
.请求方式,一般为POST(“url”).content(JSONObject.toJSONString(map)).contentType(.contentType(MediaType.APPLICATION_JSON))

测试过程如下:
1、准备测试环境
2、通过MockMvc执行请求
3.添加验证断言
4.添加结果处理器
5.得到MvcResult进行自定义断言/进行下一步的异步请求
6.卸载测试环境

第二种使用真实Web环境进行测试

在@SpringBootTest注解中设置属性 webEnvironment = WebEnvironment.RANDOM_PORT,每次运行的时候会随机选择一个可用端口。我们也可以还使用 @LoalServerPort注解用于本地端口号。下面是测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerTest3 {
@Autowired
private RestTemplate restemplate;
@Test
public void userMapping() throws Exception {
User user = new User();
user.setNickName("pj_pj");
user.setPassword("12345678");
ResponseEntity<String> responseEntity = testRestTemplate.postForEntity("/user", user, String.class);
System.out.println("Result: "+responseEntity.getBody());
System.out.println("状态码: "+responseEntity.getStatusCodeValue());
}
}

上面的代码中有一个关键的类——TestRestTemplate, TestRestTemplate是Spring的RestTemplate的一种替代品,可用于集成测试,更RestTemplate的使用功能方法类似,一般用于真实web环境测试中,关于该类更加详细的用法参考官方文档

常用注解介绍

@SpringBootTestSpringBoot的一个用于测试的注解,通过SpringApplication在测试中创建ApplicationContext
@AutoConfigureMockMvc是用于自动配置MockMvc。
@RunWith在JUnit中有很多个Runner,他们负责调用你的测试代码,每一个Runner都有各自的特殊功能,你要根据需要选择不同的Runner来运行你的测试代码。
@Before在每个测试方法前执行,一般用来初始化方法。
@After在每个测试方法后执行,在方法执行完成后要做的事情。

主要代码

引入测试jar包

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;

@Before //测试前
public void setUp() throws Exception {
//先注册一个user对象
String JSON =
"{\"nickName\":\"岗村一泽\",\"lastName\":\"岗村\",\"firstName\":\"一泽\",\"email\":\"gangcunyiza@gmail.com\",\"password\":\"12345678\",\"jpFirstName\":\"一泽\",\"jpLastName\":\"岗村\"}";
Mockito.when(userService.findByEmailAndStatus("gangcunyiza@gmail.com.com",1)).thenReturn(user);
Mockito.when(userRepository.findByNickNameAndStatus("岗村一泽",1)).thenReturn(user);
mockMvc.perform(MockMvcRequestBuilders.post("/registe")
.accept(MediaType.APPLICATION_JSON).content(JSON)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn();

}
@After //测试后
public void tearDown() throws Exception {
}
@Test
public void login() throws Exception {
String JSON = "{\"email\":\"gangcunyiza@gmail.com.com\",\"password\":\"12345678\"}";
mockMvc.perform(MockMvcRequestBuilders.post("/login")
.accept(MediaType.APPLICATION_JSON).content(JSON)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())//断言请求状态
.andReturn();
}
}

另外,如果是只想关注Web层而不是启动完整的ApplicationContext,可以考虑使用@WebMvcTest注解,该注解不能与@SpringBootTest搭配使用,而且它只关注Web层面,至于涉及到数据层的时候,需要引入相关依赖,关于这个注解更多的介绍请参阅官方文档

单元测试回滚

单元测试的时候,如果不想造成垃圾数据,可以开启事务功能,在方法或类头部添加@Transactional注解即可,在官方文档中对此也有说明。如果你想关闭回滚,只要加上 @Rollback(false)注解即可。
还有一种情况需要注意,就是如果你使用的数据库是MySQL,有时候会发现加了注解 @Transactionl也不会回滚,那么你就要查看一下你的默认引擎是不是InnoDB,如果不是就要改成 InnoDB。
MyISAM 与 InnoDB是mysql目前比较常用的两个数据库引擎,MyISAM与InnoDB的主要的不同点在于性能和事务控制上,这里简单介绍下两者的区别与转换方法:

MyISAM: MyISAM是MySQL5.5之前版本默认的数据库存储引擎,MyISAM提供高速存储和检索,以及全文搜索能力,适合数据仓库等查询频繁的应用,但不支持事务和外键,不能在表损坏后恢复数据
InnoDB: InnoDB是MySQL5.5版本的默认数据库存储引擎,InnoDB具有提交,回滚和崩溃恢复能力的事务安全,支持事务和外键,比起MyISAM,InnoDB写的处理效率差一些并且会占用更多的磁盘空间以保留数据和索引。

欣赏此文?求鼓励,求支持!