本次教程主要講解如何對Spring Boot中的Rest Service進行單元測試。以往咱們主要是使用JUnit對業務層進行單元測試,本次課程將使用一個簡單的案例來講明如何使用JUnit對Spring Boot的Rest Service進行單元測試。html
項目代碼已經上傳到GitHub倉庫中,你能夠經過如下的地址獲取示例源碼:java
下面經過一張截圖來了解如下本次課程中咱們使用到的項目結構。github
首先咱們須要位單元測試提供一個可用的Rest Controller。UserController文件爲咱們提供了一個可用於測試的Rest Controller。在UserController類中,咱們提供兩種請求類型的方法,一種是GET請求,另外一種是POST請求。而後咱們爲這兩種請求方式的方法編寫單元測試用例。web
在接下來的測試過程當中,咱們將使用Mockito來模擬請求UserService的過程,使用MockMvc來模擬請求UserController。單元測試的目的是將測試範圍儘量的縮小。在本次案例中,咱們僅對UserController中的方法進行測試。spring
咱們依然使用Spring Initializr來初始化本次課程的項目,你須要配置以下圖中的參數:數據庫
如今咱們須要提供兩個實體類:User和Role:json
User.javamarkdown
Role.javaapp
全部的應用都須要有數據的存儲,本次課程主要的重點是爲了Rest Controller的單元測試,所以使用ArrayList來充當數據庫的角色。在案例中,一個用戶能夠有多個角色,一個角色也能夠被賦予給多個用戶。用戶有ID,名字,別名和角色列表,角色具備ID,名稱和描述。在UserService類中,將提供如圖所示的公共方法。
在UserController類中,咱們將提供以下幾個公開的GET請求方法:
UserController.java類中的詳細代碼以下:
package com.ramostear.spring.boot.test.restservice.controller; import com.ramostear.spring.boot.test.restservice.model.Role; import com.ramostear.spring.boot.test.restservice.model.User; import com.ramostear.spring.boot.test.restservice.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController public class UserController { private final UserService userService; @Autowired public UserController(UserService userService){ this.userService = userService; } @GetMapping(value = "/users") public List<User> findAllStudents(){ return userService.findAllUsers(); } @GetMapping(value = "/users/{id}/roles") public List<Role> findUserRoles(@PathVariable(value = "id")String id){ return userService.findUserAllRoles(id); } } 複製代碼
咱們將使用Postman工具對上述兩個Rest API進行請求,首先向Postman地址欄輸入http://localhost:8080/users 進行測試,得到的響應信息以下:
[ { "id": "1001", "name": "ramostear", "alias": "譚朝紅", "roles": [ { "id": "1001", "name": "admin", "description": "all permissions for this role." } ] } ] 複製代碼
下圖顯示了Postman對此API進行測試的實際結果:
當咱們須要對一個Rest Controller進行單元測試時,咱們只想啓動和SpringMVC相關的組件,而沒必要要啓動全部的Web組件。咱們可使用WebMvcTest註解來解決這樣的測試需求。此註解將禁用Spring Boot的自動化配置,僅僅啓動與MVC相關的配置。下面將對測試用例中的幾個核心註解作一下介紹:
下面是測試用例的源代碼:
package com.ramostear.spring.boot.test.restservice; import com.ramostear.spring.boot.test.restservice.controller.UserController; import com.ramostear.spring.boot.test.restservice.model.Role; import com.ramostear.spring.boot.test.restservice.model.User; import com.ramostear.spring.boot.test.restservice.service.UserService; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.skyscreamer.jsonassert.JSONAssert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.RequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import java.util.ArrayList; import java.util.List; @RunWith(SpringRunner.class) @WebMvcTest(value = UserController.class,secure = false) public class UserControllerTests { private static Logger logger = LoggerFactory.getLogger(UserControllerTests.class); @Autowired private MockMvc mockMvc; @MockBean private UserService userService; @Test public void findAllUsers() throws Exception{ User user = new User(); user.setId("1001"); user.setName("ramostear"); user.setAlias("譚朝紅"); Role role = new Role(); role.setId("1001"); role.setName("admin"); role.setDescription("all permissions for this role."); List<Role> roles = new ArrayList<>(); roles.add(role); user.setRoles(roles); List<User> users = new ArrayList<>(); users.add(user); Mockito.when(userService.findAllUsers()).thenReturn(users); RequestBuilder requestBuilder = MockMvcRequestBuilders.get("/users"); MvcResult result = mockMvc.perform(requestBuilder).andReturn(); String expected = "[{\"id\":\"1001\",\"name\":\"ramostear\",\"alias\":\"譚朝紅\",\"roles\":[{\"id\":\"1001\",\"name\":\"admin\",\"description\":\"all permissions for this role.\"}]}]"; logger.info(result.getResponse().getContentAsString()); JSONAssert.assertEquals(expected,result.getResponse().getContentAsString(),false); } @Test public void findAllUserRoles() throws Exception{ Role role = new Role(); role.setId("1001"); role.setName("admin"); role.setDescription("all permissions for this role."); List<Role> roles = new ArrayList<>(); roles.add(role); Mockito.when(userService.findUserAllRoles("1001")).thenReturn(roles); RequestBuilder requestBuilder = MockMvcRequestBuilders.get("/users/1001/roles"); MvcResult result = mockMvc.perform(requestBuilder).andReturn(); String expected = "[{\"id\":\"1001\",\"name\":\"admin\",\"description\":\"all permissions for this role.\"}]"; logger.info(result.getResponse().getContentAsString()); JSONAssert.assertEquals(expected,result.getResponse().getContentAsString(),false); } } 複製代碼
Mockito.when().thenReturn():用於測試UserService在被調用時是否返回和預期一致的結果
mockMvc.perform().andReturn():mockMvc主要用於執行請求並返回響應數據
下面咱們執行上述兩個方法,看看測試結果:
兩個方法均測試經過,且控制檯也輸出了以下的日誌信息:
2019-05-10 05:36:40.567 INFO 18268 --- [ main] c.r.s.b.t.r.UserControllerTests : [{"id":"1001","name":"admin","description":"all permissions for this role."}]
2019-05-10 05:36:40.585 INFO 18268 --- [ main] c.r.s.b.t.r.UserControllerTests : [{"id":"1001","name":"ramostear","alias":"譚朝紅","roles":[{"id":"1001","name":"admin","description":"all permissions for this role."}]}]
複製代碼
如今咱們在Rest Controller中新增一個爲用戶添加新角色的方法,當角色被成功設置後將返回狀態碼爲201的一個建立資源狀態。代碼以下:
@PostMapping(value = "/users/{id}") public ResponseEntity<Object> setUserRole(@PathVariable(value = "id")String id, @RequestBody Role role){ userService.addUserRole(id,role); return new ResponseEntity<>(role, HttpStatus.CREATED); } 複製代碼
使用Postman對此接口進行請求,得到以下的響應信息:
{ "id": "1002", "name": "editor", "description": "content editor" } 複製代碼
下圖時使用Postman請求的實際結果:
在接下來的測試中,咱們將使用MockMvcRequestBuilders.post()方法來模擬請求添加用戶角色的方法,並使用accept()方法來設置數據格式,另外還需斷言請求響應的狀態值是否爲CREATED且返回的角色信息是否與預期的一致。Post方法的測試源碼以下:
@Test public void addUserRole() throws Exception{ String JSON = "{\"id\":\"1002\",\"name\":\"editor\",\"description\":\"content editor\"}"; RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/users/1001") .accept(MediaType.APPLICATION_JSON).content(JSON) .contentType(MediaType.APPLICATION_JSON); MvcResult result = mockMvc.perform(requestBuilder).andReturn(); MockHttpServletResponse response = result.getResponse(); Assert.assertEquals(HttpStatus.CREATED.value(),response.getStatus()); String expected = "{\"id\":\"1002\",\"name\":\"editor\",\"description\":\"content editor\"}"; logger.info(result.getResponse().getContentAsString()); JSONAssert.assertEquals(expected,result.getResponse().getContentAsString(),false); } 複製代碼
最後,咱們執行此測試用例方法,觀察測試結構:
經過圖咱們能夠看到,Post方法已成功經過測試。
今天的課程分享到這裏就結束了,在本次課程中,給出瞭如何測試Rest Controller的方法,同時還使用Postman來進行輔助測試。全部的測試都達到了預期的測試效果。