关于 Mockito 以及 模拟(mock)测试 的介绍请直接看下面的文章:
https://www.oschina.net/translate/mockito-a-great-mock-framework-for-java-development
https://www.tianmaying.com/tutorial/JunitForSpringBoot。
在此文主要列举了一些可能在实际中遇到的需要对我们的 Http 请求进行单元测试的例子,包括get、post和文件上传等。
环境
基于JDK1.8 + SpringBoot 1.4 + JPA,还需添加:
1 2 3 4 5 | <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> |
涉及的类和下载:点我
MockMvcController:
其中 @RestController
是 @Controller + @ResponseBody
的注释组合。
1 2 3 4 5 6 | @RestController @RequestMapping("/mock") public class MockMvcController { private final static Logger logger = LoggerFactory.getLogger(MockMvcController.class); private final static String SUCCESS = "success"; } |
MockMvcControllerTest:
单元测试用到的 get()、post()、fileupload()
是 MockMvcRequestBuilders静态方法;content()、status()
则是 MockMvcResultMatchers 的方法。
1 2 3 4 5 6 7 8 9 10 11 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @RunWith(SpringRunner.class) @WebMvcTest(MockMvcController.class) public class MockMvcControllerTest { private final static ObjectMapper objectMapper = new ObjectMapper(); @Autowired private MockMvc mockMvc; } |
Role:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @Entity @Table(name = "roles") public class Role implements Serializable{ private static final long serialVersionUID = -3181227830551232697L; @Id @GeneratedValue Integer id; String name; public Role(){ } public Role(Integer id,String name) { this.name = name; this.id = id; } //省略getter、setter和toString() } |
get 请求
在 controller 中写添加一个 get 请求方法:
1 2 3 4 5 | @RequestMapping("/get") String get(@RequestParam String method){ logger.info("method: {}", method); return SUCCESS; } |
单元测试:
1 2 3 4 5 6 | @Test public void testGet() throws Exception{ mockMvc.perform(get("/mock/get?method=testGet")) //参数也可以使用 param("method", "testGet") .andExpect(status().is(200)) // 请求的状态码是否符合预期 .andExpect(content().string("success"));// 返回的内容是否符合预期 } |
测试结果:
method: testGet
post 请求
HttpServletRequest 作为参数
请求:
1 2 3 4 5 | @RequestMapping(value = "/http", method = RequestMethod.POST) String http(HttpServletRequest request, HttpServletResponse rsponse){ logger.info("Request RemoteAddr: {};", request.getRemoteAddr()); return SUCCESS; } |
单元测试:
1 2 3 4 5 6 7 | @Test public void testHttp() throws Exception{ mockMvc.perform(post("/mock/http").with(request -> { request.setRemoteAddr("192.168.0.1"); return request; })); } |
测试结果:
Request RemoteAddr: 192.168.0.1;
Bean 作为参数
Http 请求:
1 2 3 4 5 | @RequestMapping(value = "/postByBean", method = RequestMethod.POST) String postByBean(Role role, String method){ logger.info("role: {}; method: {}", role.toString(), method); return SUCCESS; } |
单元测试:
1 2 3 4 5 6 7 8 9 | @Test public void testPostByBean() throws Exception{ mockMvc.perform(post("/mock/postByBean") .param("id", "2") .param("name", "ROLE_USER") .param("method", "postByBean")) .andExpect(status().is(200)) .andExpect(content().string("success")); } |
测试结果:
role: Role{id=2, name=’ROLE_USER’}; method: postByBean
json 作为参数
Http 请求:
1 2 3 4 5 | @RequestMapping(value = "/postByJson", method = RequestMethod.POST) String postByJson(@RequestBody Role role, String method){ logger.info("role: {}; method: {}", role.toString(), method); return SUCCESS; } |
单元测试:
1 2 3 4 5 6 7 8 | @Test public void testPostByJson() throws Exception{ mockMvc.perform(post("/mock/post") .param("method", "postByJson") .content(objectMapper.writeValueAsString(new Role(2, "ROLE_USER"))).contentType(MediaType.APPLICATION_JSON)) // json 参数和类型 .andExpect(status().is(200)) .andExpect(content().string("success")); } |
测试结果:
role: Role{id=2, name=’ROLE_USER’}; method: postByJson
文件上传(fileUpload)
Http 请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @RequestMapping(value = "/upload", method = RequestMethod.POST) String upload(@RequestPart(value = "role") Role role, @RequestParam(value = "file", required = true) List<MultipartFile> files) { logger.info("role: {};", role.toString()); files.stream().forEach(file -> { try { logger.info("filename: {}; originalFilename: {}; content: {}" , file.getName() , file.getOriginalFilename() , IOUtils.toString(file.getInputStream())); } catch (IOException e) { e.printStackTrace(); } }); return SUCCESS; } |
单元测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @Test public void testUpload() throws Exception{ MockMultipartFile jsonFile = new MockMultipartFile("role", "", "application/json","{\"id\":1,\"name\":\"test\"}".getBytes()); //json文件 MockMultipartFile file1 = new MockMultipartFile("file", "filename.txt", "text/plain", "content in file1".getBytes()); //普通文件 MockMultipartFile file2 = new MockMultipartFile("file", "other-file.data", "text/plain", "content in file2".getBytes()); mockMvc.perform(fileUpload("/mock/upload") .file(jsonFile) .file(file1).file(file2)) .andExpect(status().is(200)) .andExpect(content().string("success")); } |
测试结果:
role: Role{id=2, name=’ROLE_USER’}; id: 2; name: ROLE_USER
filename: file; originalFilename: filename.txt; content: content in file1
filename: file; originalFilename: other-file.data; file content: content in file2
REST 请求及事务的注入
增加一个实现 JPA 的 Repository 类,不必有具体的实现方法。
1 2 | public interface RoleRepository extends JpaRepository<Role, Integer> { } |
在 Controller 中添加一个实现 Rest 风格的请求方法,并注入 RoleRepository
:
1 2 3 4 5 6 7 8 9 | @Autowired private RoleRepository roleRepository; @RequestMapping("/rest/{id}") Role rest(@PathVariable Integer id){ Role role = roleRepository.findOne(id); logger.info("Role Info: {}", role.toString()); return role; } |
单元测试:@MockBean
此注释被用来添加 mock 对象到 Spring ApplicationContext中。 given()
属于 mockito-core 包,模拟的是请求方法中涉及的数据库操作。
1 2 3 4 5 6 7 8 9 10 | @MockBean private RoleRepository roleRepository; @Test public void testRest()throws Exception{ given(this.roleRepository.findOne(2)).willReturn(new Role(2, "ROLE_USER")); String contec.perform(get("/mock/rest/2")) .andExpect(status().is(200)).andReturn().getResponse().getContentAsString(); System.out.println(content); } |
测试结果:
{
“id” : 2,
“name” : “ROLE_USER”
}
除了get()
、post()
和fileUpload()
外还有patch()
、put()
和 delete()
测试 REST 格式请求的方法。
参考:
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html
http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/mock/mockito/MockBean.html
http://stackoverflow.com/questions/21800726/using-spring-mvc-test-to-unit-test-multipart-post-request