Mockito编写Controller层单元测试
程序员文章站
2022-03-02 18:13:25
...
测试团队要求写单元测试,但是Mockito网上相关的文档不是很多,基本都是入门性质的没有更深层次的使用案例,而且Mockito本身功能也在不断的完善,导致写起来比较费劲,好多地方完全靠猜。摸索之下算是完成了,把踩过的坑记录一下,万一有人需要呢。
Controller层的单元测试比较简单,主要思路是mock业务层返回值,然后模拟调用接口,看两个例子就明白了。
Controller.java
@RestController
public class Controller {
@Autowired
private final IndiskService indiskService;
private static final String AUTHORIZATION = "Authorization";
@PostMapping("/ebs/{version}/volumes/{volume_id}/action/attach")
public ReturnMsgVO attach(@PathVariable("volume_id") String volume_id,
@RequestBody JSONObject jsonObject,
HttpServletRequest request) {
String keycloakToken = request.getHeader(AUTHORIZATION);
AttachDetachExtendVO attachDetachExtendVO = new AttachDetachExtendVO();
//do something......
//正确返回200
return indiskService.attachVolume(attachDetachExtendVO, request, keycloakToken);
}
@DeleteMapping("/ebs/{version}/volumes/{volume_id}/soft")
public ReturnMsgVO softDeleteVolume(@PathVariable(value = "volume_id") String volumeId,
@RequestBody String orderJson,
HttpServletRequest request) {
String keycloakToken = request.getHeader(AUTHORIZATION);
//正确返回202,消息体为{"code":"202","message":null,"data":null}
return indiskService.softDeleteVolume(volumeId, keycloakToken, orderJson);
}
}
需要指出一点接口有token认证,认证字段存在于Header中,字段名为Authorization。
再来看一下对应的测试类,注意import时采用的是static引入。
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public class ControllerTest {
private static final String AUTHORIZATION = "Authorization";
private MockMvc mvc;
@Mock
//要mock被测类中依赖的对象使用@Mock注解
private ServiceImpl serviceImpl;
@InjectMocks
//被测类本身使用@InjectMocks注解
private Controller controller;
@Before
public void setup() {
//初始化
MockitoAnnotations.initMocks(this);
//构建mvc环境
mvc = MockMvcBuilders.standaloneSetup(controller).build();
}
@Test
public void testSoftDeleteVolume() throws Exception {
//mock方法行为
Mockito.when(serviceImpl.softDeleteVolume("1", "2", "3"))
.thenReturn(ReturnMsgVO.builder().code("202").build());
//模拟接口调用
this.mvc.perform(delete("/ebs/v1/volumes/1/soft")
.header(AUTHORIZATION, "2").content("3"))
.andExpect(status().isOk())
//对接口响应进行验证
.andExpect(content().json("{\"code\":\"202\",\"message\":null,\"data\":null}"));
}
@Test
public void attach() throws Exception {
Mockito.when(serviceImpl.attachVolume(Mockito.any(), Mockito.any(), Mockito.anyString()))
.thenReturn(ReturnMsgUtil.success());
mvc.perform(post("/ebs/v1/volumes/123/action/attach")
.header(AUTHORIZATION, "123")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"os-attach\":{\"instance_uuid\":\"123\"}}"))
.andExpect(status().isOk());
}
}
总结一下要点:
- Controller层单元测试不需要启动应用,也就不需要给类名加任何Test相关的注解
- 注意import相关包的方式,有些人喜欢以static的方式引入,在网上参考他人代码时务必注意一下
- mock方法行为时,需注意调用时的url请求参数应该与mock时的参数保持一致,否则mock不成功。(若使用AnyString()等方法mock可无视此条)
- A类中依赖注入了B类,在写A的测试类时要注意:对A使用
@InjectMocks
注解,对于B使用@Mock
注解 - 不要忘记initMocks
推荐阅读
-
Spring Boot从Controller层进行单元测试的实现
-
[单元测试]jmockit+SpringMVC整合并测试Controller层方法(三)
-
Mockito单元测试之SpringMvc controller
-
Spring Boot Controller单元测试的编写
-
单元测试Junit Test 聚焦Controller层 Java版
-
springboot 单元测试 (controller层) 方法 -- MockMvc
-
controller 和dao_[易学springboot]对controller层进行单元测试
-
单元测试总结:Dao层、Service层和Controller层
-
Mockito编写单元测试
-
关于SpringBoot框架下的service层单元测试问题(mockito)