關於 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