利用 Junit + MockMvc + Mockito 對 Http 請求進行單元測試

關於 Mockito 以及 模擬(mock)測試 的介紹請直接看下面的文章:
https://www.oschina.net/translate/mockito-a-great-mock-framework-for-java-development
https://www.tianmaying.com/tutorial/JunitForSpringBoot。

在此文主要列舉了一些可能在實際中遇到的需要對我們的 Http 請求進行單元測試的例子,包括getpost文件上傳等。

環境

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章