您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關Spring MVC接口如何使用Mock進行測試,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
1. 前言
在Java開發中接觸的開發者大多數不太注重對接口的測試,結果在聯調對接中出現各種問題。也有的使用Postman等工具進行測試,雖然在使用上沒有什么問題,如果接口增加了權限測試起來就比較惡心了。所以建議在單元測試中測試接口,保證在交付前先自測接口的健壯性。今天就來分享一下胖哥在開發中是如何對Spring MVC接口進行測試的。
在開始前請務必確認添加了Spring Boot Test相關的組件,在最新的版本中應該包含以下依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency>
本文是在Spring Boot 2.3.4.RELEASE下進行的。
2. 單獨測試控制層
如果我們只需要對控制層接口(Controller)進行測試,且該接口不依賴@Service、@Component等注解聲明的Spring Bean時,可以借助@WebMvcTest來啟用只針對Web控制層的測試,例如
@WebMvcTest class CustomSpringInjectApplicationTests { @Autowired MockMvc mockMvc; @SneakyThrows @Test void contextLoads() { mockMvc.perform(MockMvcRequestBuilders.get("/foo/map")) .andExpect(ResultMatcher.matchAll(status().isOk(), content().contentType(MediaType.APPLICATION_JSON), jsonPath("$.test", Is.is("hello")))) .andDo(MockMvcResultHandlers.print()); } }
這種方式要快的多,它只加載了應用程序的一小部分。但是如果你涉及到服務層這種方式是不湊效的,我們就需要另一種方式了。
3. 整體測試
大多數Spring Boot下的接口測試是整體而又全面的測試,涉及到控制層、服務層、持久層等方方面面,所以需要加載比較完整的Spring Boot上下文。這時我們可以這樣做,聲明一個抽象的測試基類:
package cn.felord.custom; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; /** * 測試基類, * @author felord.cn */ @SpringBootTest @AutoConfigureMockMvc abstract class CustomSpringInjectApplicationTests { /** * The Mock mvc. */ @Autowired MockMvc mockMvc; // 其它公共依賴和處理方法 }
只有當@AutoConfigureMockMvc存在時MockMvc才會被注入Spring IoC。
然后針對具體的控制層進行如下測試代碼的編寫:
package cn.felord.custom; import lombok.SneakyThrows; import org.hamcrest.core.Is; import org.junit.jupiter.api.Test; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; /** * 測試FooController. * * @author felord.cn */ public class FooTests extends CustomSpringInjectApplicationTests { /** * /foo/map接口測試. */ @SneakyThrows @Test void contextLoads() { mockMvc.perform(MockMvcRequestBuilders.get("/foo/map")) .andExpect(ResultMatcher.matchAll(status().isOk(), content().contentType(MediaType.APPLICATION_JSON), jsonPath("$.test", Is.is("bar")))) .andDo(MockMvcResultHandlers.print()); } }
4. MockMvc測試
集成測試時,希望能夠通過輸入URL對Controller進行測試,如果通過啟動服務器,建立http client進行測試,這樣會使得測試變得很麻煩,比如,啟動速度慢,測試驗證不方便,依賴網絡環境等,為了可以對Controller進行測試就引入了MockMvc。
MockMvc實現了對Http請求的模擬,能夠直接使用網絡的形式,轉換到Controller的調用,這樣可以使得測試速度快、不依賴網絡環境,而且提供了一套驗證的工具,這樣可以使得請求的驗證統一而且很方便。接下來我們來一步步構造一個測試的模擬請求,假設我們存在一個下面這樣的接口:
@RestController @RequestMapping("/foo") public class FooController { @Autowired private MyBean myBean; @GetMapping("/user") public Map<String, String> bar(@RequestHeader("Api-Version") String apiVersion, User user) { Map<String, String> map = new HashMap<>(); map.put("test", myBean.bar()); map.put("version", apiVersion); map.put("username", user.getName()); //todo your business return map; } }
參數設定為name=felord.cn&age=18,那么對應的HTTP報文是這樣的:
GET /foo/user?name=felord.cn&age=18 HTTP/1.1
Host: localhost:8888
Api-Version: v1
可以預見的返回值為:
{ "test": "bar", "version": "v1", "username": "felord.cn" }
事實上對接口的測試可以分為以下幾步。
構建請求
構建請求由MockMvcRequestBuilders負責,他提供了請求方法(Method),請求頭(Header),請求體(Body),參數(Parameters),會話(Session)等所有請求的屬性構建。/foo/user接口的請求可以轉換為:
MockMvcRequestBuilders.get("/foo/user")
.param("name", "felord.cn")
.param("age", "18")
.header("Api-Version", "v1")
執行Mock請求
然后由MockMvc執行Mock請求:
mockMvc.perform(MockMvcRequestBuilders.get("/foo/user")
.param("name", "felord.cn")
.param("age", "18")
.header("Api-Version", "v1"))
對結果進行處理
請求結果被封裝到ResultActions對象中,它封裝了多種讓我們對Mock請求結果進行處理的方法。
對結果進行預期期望
ResultActions#andExpect(ResultMatcher matcher)方法負責對響應的結果的進行預期期望,看看是否符合測試的期望值。參數ResultMatcher負責從響應對象中提取我們需要期望的部位進行預期比對。
假如我們期望接口/foo/user返回的是JSON,并且HTTP狀態為200,同時響應體包含了version=v1的值,我們應該這么聲明:
ResultMatcher.matchAll(MockMvcResultMatchers.status().isOk(),
MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON),
MockMvcResultMatchers.jsonPath("$.version", Is.is("v1")));
JsonPath是一個強大的JSON解析類庫,請通過其項目倉庫https://github.com/json-path/JsonPath了解。
對響應進行處理
ResultActions#andDo(ResultHandler handler)方法負責對整個請求/響應進行打印或者log輸出、流輸出,由MockMvcResultHandlers工具類提供這些方法。我們可以通過以上三種途徑來查看請求響應的細節。
例如/foo/user接口:
MockHttpServletRequest: HTTP Method = GET Request URI = /foo/user Parameters = {name=[felord.cn], age=[18]} Headers = [Api-Version:"v1"] Body = null Session Attrs = {} Handler: Type = cn.felord.xbean.config.FooController Method = cn.felord.xbean.config.FooController#urlEncode(String, Params) Async: Async started = false Async result = null Resolved Exception: Type = null ModelAndView: View name = null View = null Model = null FlashMap: Attributes = null MockHttpServletResponse: Status = 200 Error message = null Headers = [Content-Type:"application/json"] Content type = application/json Body = {"test":"bar","version":"v1","username":"felord.cn"} Forwarded URL = null Redirected URL = null Cookies = []
獲取返回結果
如果你希望進一步處理響應的結果,也可以通過ResultActions#andReturn()拿到MvcResult類型的結果進行進一步的處理。
完整的測試過程
通常andExpect是我們必然會選擇的,而andDo和andReturn在某些場景下會有用,它們兩個是可選的。我們把上面的連在一起。
@Autowired MockMvc mockMvc; @SneakyThrows @Test void contextLoads() { mockMvc.perform(MockMvcRequestBuilders.get("/foo/user") .param("name", "felord.cn") .param("age", "18") .header("Api-Version", "v1")) .andExpect(ResultMatcher.matchAll(status().isOk(), content().contentType(MediaType.APPLICATION_JSON), jsonPath("$.version", Is.is("v1")))) .andDo(MockMvcResultHandlers.print()); }
這種流式的接口單元測試從語義上看也是比較好理解的,你可以使用各種斷言、正例、反例測試你的接口,最終讓你的接口更加健壯。
看完上述內容,你們對Spring MVC接口如何使用Mock進行測試有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。