av一区二区在线观看_亚洲男人的天堂网站_日韩亚洲视频_在线成人免费_欧美日韩精品免费观看视频_久草视

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

基于Mock測(cè)試Spring MVC接口過程解析

瀏覽:29日期:2023-08-01 15:59:31

1. 前言

在Java開發(fā)中接觸的開發(fā)者大多數(shù)不太注重對(duì)接口的測(cè)試,結(jié)果在聯(lián)調(diào)對(duì)接中出現(xiàn)各種問題。也有的使用Postman等工具進(jìn)行測(cè)試,雖然在使用上沒有什么問題,如果接口增加了權(quán)限測(cè)試起來就比較惡心了。所以建議在單元測(cè)試中測(cè)試接口,保證在交付前先自測(cè)接口的健壯性。今天就來分享一下胖哥在開發(fā)中是如何對(duì)Spring MVC接口進(jìn)行測(cè)試的。

在開始前請(qǐng)務(wù)必確認(rèn)添加了Spring Boot Test相關(guān)的組件,在最新的版本中應(yīng)該包含以下依賴:

<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下進(jìn)行的。

2. 單獨(dú)測(cè)試控制層

如果我們只需要對(duì)控制層接口(Controller)進(jìn)行測(cè)試,且該接口不依賴@Service、@Component等注解聲明的Spring Bean時(shí),可以借助@WebMvcTest來啟用只針對(duì)Web控制層的測(cè)試,例如

@WebMvcTestclass 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()); }}

這種方式要快的多,它只加載了應(yīng)用程序的一小部分。但是如果你涉及到服務(wù)層這種方式是不湊效的,我們就需要另一種方式了。

3. 整體測(cè)試

大多數(shù)Spring Boot下的接口測(cè)試是整體而又全面的測(cè)試,涉及到控制層、服務(wù)層、持久層等方方面面,所以需要加載比較完整的Spring Boot上下文。這時(shí)我們可以這樣做,聲明一個(gè)抽象的測(cè)試基類:

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;/** * 測(cè)試基類, * @author felord.cn */@SpringBootTest@AutoConfigureMockMvcabstract class CustomSpringInjectApplicationTests { /** * The Mock mvc. */ @Autowired MockMvc mockMvc; // 其它公共依賴和處理方法 }

只有當(dāng)@AutoConfigureMockMvc存在時(shí)MockMvc才會(huì)被注入Spring IoC。

然后針對(duì)具體的控制層進(jìn)行如下測(cè)試代碼的編寫:

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.*;/** * 測(cè)試FooController. * * @author felord.cn */public class FooTests extends CustomSpringInjectApplicationTests { /** * /foo/map接口測(cè)試. */ @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測(cè)試

集成測(cè)試時(shí),希望能夠通過輸入U(xiǎn)RL對(duì)Controller進(jìn)行測(cè)試,如果通過啟動(dòng)服務(wù)器,建立http client進(jìn)行測(cè)試,這樣會(huì)使得測(cè)試變得很麻煩,比如,啟動(dòng)速度慢,測(cè)試驗(yàn)證不方便,依賴網(wǎng)絡(luò)環(huán)境等,為了可以對(duì)Controller進(jìn)行測(cè)試就引入了MockMvc。

MockMvc實(shí)現(xiàn)了對(duì)Http請(qǐng)求的模擬,能夠直接使用網(wǎng)絡(luò)的形式,轉(zhuǎn)換到Controller的調(diào)用,這樣可以使得測(cè)試速度快、不依賴網(wǎng)絡(luò)環(huán)境,而且提供了一套驗(yàn)證的工具,這樣可以使得請(qǐng)求的驗(yàn)證統(tǒng)一而且很方便。接下來我們來一步步構(gòu)造一個(gè)測(cè)試的模擬請(qǐng)求,假設(shè)我們存在一個(gè)下面這樣的接口:

@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; }}

參數(shù)設(shè)定為name=felord.cn&age=18,那么對(duì)應(yīng)的HTTP報(bào)文是這樣的:

GET /foo/user?name=felord.cn&age=18 HTTP/1.1Host: localhost:8888Api-Version: v1

可以預(yù)見的返回值為:

{ 'test': 'bar', 'version': 'v1', 'username': 'felord.cn'}

事實(shí)上對(duì)接口的測(cè)試可以分為以下幾步。

構(gòu)建請(qǐng)求

構(gòu)建請(qǐng)求由MockMvcRequestBuilders負(fù)責(zé),他提供了請(qǐng)求方法(Method),請(qǐng)求頭(Header),請(qǐng)求體(Body),參數(shù)(Parameters),會(huì)話(Session)等所有請(qǐng)求的屬性構(gòu)建。/foo/user接口的請(qǐng)求可以轉(zhuǎn)換為:

MockMvcRequestBuilders.get('/foo/user').param('name', 'felord.cn').param('age', '18').header('Api-Version', 'v1')

執(zhí)行Mock請(qǐng)求

然后由MockMvc執(zhí)行Mock請(qǐng)求:

mockMvc.perform(MockMvcRequestBuilders.get('/foo/user').param('name', 'felord.cn').param('age', '18').header('Api-Version', 'v1'))

對(duì)結(jié)果進(jìn)行處理

請(qǐng)求結(jié)果被封裝到ResultActions對(duì)象中,它封裝了多種讓我們對(duì)Mock請(qǐng)求結(jié)果進(jìn)行處理的方法。

對(duì)結(jié)果進(jìn)行預(yù)期期望

ResultActions#andExpect(ResultMatcher matcher)方法負(fù)責(zé)對(duì)響應(yīng)的結(jié)果的進(jìn)行預(yù)期期望,看看是否符合測(cè)試的期望值。參數(shù)ResultMatcher負(fù)責(zé)從響應(yīng)對(duì)象中提取我們需要期望的部位進(jìn)行預(yù)期比對(duì)。

假如我們期望接口/foo/user返回的是JSON,并且HTTP狀態(tài)為200,同時(shí)響應(yīng)體包含了version=v1的值,我們應(yīng)該這么聲明:

ResultMatcher.matchAll(MockMvcResultMatchers.status().isOk(),MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON),MockMvcResultMatchers.jsonPath('$.version', Is.is('v1')));

JsonPath是一個(gè)強(qiáng)大的JSON解析類庫(kù),請(qǐng)通過其項(xiàng)目倉(cāng)庫(kù)https://github.com/json-path/JsonPath了解。

對(duì)響應(yīng)進(jìn)行處理

ResultActions#andDo(ResultHandler handler)方法負(fù)責(zé)對(duì)整個(gè)請(qǐng)求/響應(yīng)進(jìn)行打印或者log輸出、流輸出,由MockMvcResultHandlers工具類提供這些方法。我們可以通過以上三種途徑來查看請(qǐng)求響應(yīng)的細(xì)節(jié)。

例如/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 = nullResolved Exception: Type = nullModelAndView: View name = null View = null Model = nullFlashMap: Attributes = nullMockHttpServletResponse: 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 = []

獲取返回結(jié)果

如果你希望進(jìn)一步處理響應(yīng)的結(jié)果,也可以通過ResultActions#andReturn()拿到MvcResult類型的結(jié)果進(jìn)行進(jìn)一步的處理。

完整的測(cè)試過程

通常andExpect是我們必然會(huì)選擇的,而andDo和andReturn在某些場(chǎng)景下會(huì)有用,它們兩個(gè)是可選的。我們把上面的連在一起。

@AutowiredMockMvc mockMvc;@SneakyThrows@Testvoid 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()); }

這種流式的接口單元測(cè)試從語義上看也是比較好理解的,你可以使用各種斷言、正例、反例測(cè)試你的接口,最終讓你的接口更加健壯。

5. 總結(jié)

一旦你熟練了這種方式,你編寫的接口將更加具有權(quán)威性而不會(huì)再漏洞百出,甚至有時(shí)候你也可以使用Mock來設(shè)計(jì)接口,使之更加貼合業(yè)務(wù)。所以CRUD不是完全沒有技術(shù)含量,高質(zhì)量高效率的CRUD往往需要這種工程化的單元測(cè)試來支撐。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持好吧啦網(wǎng)。

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 亚洲国产一区二区三区在线观看 | 中文字幕亚洲欧美 | 91中文字幕在线观看 | 午夜精品一区二区三区在线视频 | 亚洲一区二区在线电影 | 欧美日韩精品一区二区天天拍 | а_天堂中文最新版地址 | 国产精品一区网站 | 综合色播 | 精品欧美黑人一区二区三区 | 懂色av一区二区三区在线播放 | 一级黄色毛片子 | 国产伦精品一区二区三区精品视频 | 91欧美激情一区二区三区成人 | 黄色一级视频 | 夜夜夜久久久 | 亚洲欧美日韩在线 | cao在线| 欧美一级三级在线观看 | 久久免费电影 | 欧美99久久精品乱码影视 | 欧一区| 久久国产一区二区 | 国产精品99久久久精品免费观看 | 精品国产成人 | 欧美黑人狂野猛交老妇 | 99re6热在线精品视频播放 | 97超碰在线免费 | 亚洲精品乱码久久久久久按摩观 | 国产一区视频在线 | 日韩一区不卡 | 天天天操| 中文字幕国产在线 | 美女国产精品 | 五月婷婷激情网 | 在线一区二区观看 | www.国产精| 可以在线观看av的网站 | 午夜网| www.成人.com| 毛片站 |