日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

javascript

Spring MVC控制器的单元测试:REST API

發(fā)布時(shí)間:2023/12/3 javascript 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring MVC控制器的单元测试:REST API 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Spring MVC提供了一種創(chuàng)建REST API的簡(jiǎn)便方法。 但是,為這些API編寫全面而快速的單元測(cè)試一直很麻煩。 Spring MVC測(cè)試框架的發(fā)布使我們可以編寫可讀,全面且快速的單元測(cè)試。

這篇博客文章描述了如何使用Spring MVC Test框架編寫REST API的單元測(cè)試。 在這篇博客中,我們將為控制器方法編寫單元測(cè)試,這些方法為待辦事項(xiàng)提供CRUD功能。

讓我們開始吧。

使用Maven獲取所需的依賴關(guān)系

通過將以下依賴項(xiàng)聲明添加到我們的POM文件中,我們可以獲得所需的測(cè)試依賴項(xiàng):

  • Hamcrest 1.3( hamcrest-all )。 在為響應(yīng)編寫斷言時(shí),我們使用Hamcrest匹配器。
  • Junit 4.11。 我們需要排除hamcrest-core依賴性,因?yàn)槲覀円呀?jīng)添加了hamcrest-all依賴性。
  • Mockito 1.9.5( mockito-core )。 我們使用Mockito作為我們的模擬庫(kù)。
  • Spring測(cè)試3.2.3發(fā)布
  • JsonPath 0.8.1( json-path和json-path-assert )。 在為REST API返回的JSON文檔編寫斷言時(shí),我們使用JsonPath。

相關(guān)的依賴項(xiàng)聲明如下所示:

<dependency><groupId>org.hamcrest</groupId><artifactId>hamcrest-all</artifactId><version>1.3</version><scope>test</scope> </dependency> <dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope><exclusions><exclusion><artifactId>hamcrest-core</artifactId><groupId>org.hamcrest</groupId></exclusion></exclusions> </dependency> <dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>1.9.5</version><scope>test</scope> </dependency> <dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>3.2.3.RELEASE</version><scope>test</scope> </dependency> <dependency><groupId>com.jayway.jsonpath</groupId><artifactId>json-path</artifactId><version>0.8.1</version><scope>test</scope> </dependency> <dependency><groupId>com.jayway.jsonpath</groupId><artifactId>json-path-assert</artifactId><version>0.8.1</version><scope>test</scope> </dependency>

讓我們繼續(xù)討論一下單元測(cè)試的配置。

配置我們的單元測(cè)試

我們將在此博客文章中編寫的單元測(cè)試使用基于Web應(yīng)用程序上下文的配置。 這意味著我們通過使用應(yīng)用程序上下文配置類或XML配置文件來配置Spring MVC基礎(chǔ)結(jié)構(gòu)。

因?yàn)楸窘坛痰牡谝徊糠置枋隽伺渲脩?yīng)用程序的應(yīng)用程序上下文時(shí)應(yīng)遵循的原則,所以本博文中未討論此問題。

但是,我們必須在這里解決一件事。

配置示例應(yīng)用程序的Web層的應(yīng)用程序上下文配置類(或文件)不會(huì)創(chuàng)建異常解析器bean。 本教程前面部分中使用的SimpleMappingExceptionResolver類將異常類名稱映射到拋出配置的異常時(shí)呈現(xiàn)的視圖。

如果我們正在實(shí)現(xiàn)“常規(guī)” Spring MVC應(yīng)用程序,那么這是有道理的。 但是,如果要實(shí)現(xiàn)REST API,則希望將異常轉(zhuǎn)換為HTTP狀態(tài)代碼。 默認(rèn)情況下,此行為由ResponseStatusExceptionResolver類提供。

我們的示例應(yīng)用程序還具有一個(gè)自定義異常處理程序類,該類以@ControllerAdvice批注進(jìn)行批注 。 此類處理驗(yàn)證錯(cuò)誤和應(yīng)用程序特定的異常。 我們將在本博客文章的后面部分詳細(xì)討論此類。

讓我們繼續(xù)前進(jìn),了解如何為REST API編寫單元測(cè)試。

編寫REST API的單元測(cè)試

在開始為REST API編寫單元測(cè)試之前,我們需要了解兩點(diǎn):

  • 我們需要知道Spring MVC Test框架的核心組件是什么。 這些組件在本教程的第二部分中進(jìn)行了描述。
  • 我們需要知道如何使用JsonPath表達(dá)式編寫JSON文檔的斷言。 我們可以通過閱讀我的博客文章獲得此信息,該文章描述了如何使用JsonPath編寫干凈的斷言 。

接下來,我們將看到運(yùn)行中的Spring MVC Test框架,并為以下控制器方法編寫單元測(cè)試:

  • 第一個(gè)控制器方法返回待辦事項(xiàng)列表。
  • 第二種控制器方法返回單個(gè)待辦事項(xiàng)的信息。
  • 第三種控制器方法將新的待辦事項(xiàng)條目添加到數(shù)據(jù)庫(kù),并返回添加的待辦事項(xiàng)條目。

獲取待辦事項(xiàng)

第一個(gè)控制器方法返回從數(shù)據(jù)庫(kù)中找到的待辦事項(xiàng)列表。 讓我們先來看一下該方法的實(shí)現(xiàn)。

預(yù)期行為

通過執(zhí)行以下步驟來實(shí)現(xiàn)將所有待辦事項(xiàng)返回到數(shù)據(jù)庫(kù)的控制器方法:

  • 它處理發(fā)送到url'/ api / todo'的GET請(qǐng)求。
  • 它通過調(diào)用TodoService接口的findAll()方法獲取Todo對(duì)象的列表。 此方法返回存儲(chǔ)在數(shù)據(jù)庫(kù)中的所有待辦事項(xiàng)。 這些待辦事項(xiàng)條目總是以相同的順序返回。
  • 它將接收到的列表轉(zhuǎn)換為TodoDTO對(duì)象的列表。
  • 它返回包含TodoDTO對(duì)象的列表。
  • TodoController類的相關(guān)部分如下所示:

    import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*;import java.util.ArrayList; import java.util.List;@Controller public class TodoController {private TodoService service;@RequestMapping(value = "/api/todo", method = RequestMethod.GET)@ResponseBodypublic List<TodoDTO> findAll() {List<Todo> models = service.findAll();return createDTOs(models);}private List<TodoDTO> createDTOs(List<Todo> models) {List<TodoDTO> dtos = new ArrayList<>();for (Todo model: models) {dtos.add(createDTO(model));}return dtos;}private TodoDTO createDTO(Todo model) {TodoDTO dto = new TodoDTO();dto.setId(model.getId());dto.setDescription(model.getDescription());dto.setTitle(model.getTitle());return dto;} }

    當(dāng)返回TodoDTO對(duì)象列表時(shí),Spring MVC將此列表轉(zhuǎn)換為包含對(duì)象集合的JSON文檔。 返回的JSON文檔如下所示:

    [{"id":1,"description":"Lorem ipsum","title":"Foo"},{"id":2,"description":"Lorem ipsum","title":"Bar"} ]

    讓我們繼續(xù)并編寫一個(gè)單元測(cè)試,以確保此控制器方法按預(yù)期工作。

    測(cè)試:找到待辦事項(xiàng)

    通過執(zhí)行以下步驟,我們可以為此控制器方法編寫單元測(cè)試:

  • 創(chuàng)建測(cè)試數(shù)據(jù),該數(shù)據(jù)將在調(diào)用TodoService接口的findAll()方法時(shí)返回。 我們通過使用測(cè)試數(shù)據(jù)構(gòu)建器類來創(chuàng)建測(cè)試數(shù)據(jù)。
  • 配置我們的模擬對(duì)象,使其在調(diào)用findAll()方法時(shí)返回創(chuàng)建的測(cè)試數(shù)據(jù)。
  • 執(zhí)行GET請(qǐng)求以獲取網(wǎng)址“ / api / todo”。
  • 驗(yàn)證是否返回HTTP狀態(tài)代碼200。
  • 確認(rèn)響應(yīng)的內(nèi)容類型為“ application / json”,其字符集為“ UTF-8”。
  • 使用JsonPath表達(dá)式$獲取待辦事項(xiàng)的集合,并確保返回兩個(gè)待辦事項(xiàng)。
  • 通過使用JsonPath表達(dá)式$ [0] .id , $ [0] .description和$ [0] .title來獲取第一個(gè)todo條目的id , 描述和標(biāo)題 。 驗(yàn)證是否返回了正確的值。
  • 通過使用JsonPath表達(dá)式$ [1] .id , $ [1] .description和$ [1] .title來獲取第二個(gè)待辦事項(xiàng)的ID , 描述和標(biāo)題。 驗(yàn)證是否返回了正確的值。
  • 驗(yàn)證TodoService接口的findAll()方法僅被調(diào)用一次。
  • 確保在測(cè)試過程中沒有調(diào)用模擬對(duì)象的其他方法。
  • 我們的單元測(cè)試的源代碼如下所示:

    import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc;import java.util.Arrays;import static org.hamcrest.Matchers.*; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {TestContext.class, WebAppContext.class}) @WebAppConfiguration public class TodoControllerTest {private MockMvc mockMvc;@Autowiredprivate TodoService todoServiceMock;//Add WebApplicationContext field here.//The setUp() method is omitted.@Testpublic void findAll_TodosFound_ShouldReturnFoundTodoEntries() throws Exception {Todo first = new TodoBuilder().id(1L).description("Lorem ipsum").title("Foo").build();Todo second = new TodoBuilder().id(2L).description("Lorem ipsum").title("Bar").build();when(todoServiceMock.findAll()).thenReturn(Arrays.asList(first, second));mockMvc.perform(get("/api/todo")).andExpect(status().isOk()).andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8)).andExpect(jsonPath("$", hasSize(2))).andExpect(jsonPath("$[0].id", is(1))).andExpect(jsonPath("$[0].description", is("Lorem ipsum"))).andExpect(jsonPath("$[0].title", is("Foo"))).andExpect(jsonPath("$[1].id", is(2))).andExpect(jsonPath("$[1].description", is("Lorem ipsum"))).andExpect(jsonPath("$[1].title", is("Bar")));verify(todoServiceMock, times(1)).findAll();verifyNoMoreInteractions(todoServiceMock);} }

    我們的單元測(cè)試使用一個(gè)稱為APPLICATION_JSON_UTF8的常量,該常量在TestUtil類中聲明。 該常量的值是MediaType對(duì)象,其內(nèi)容類型為“ application / json”,字符集為“ UTF-8”。

    TestUtil類的相關(guān)部分如下所示:

    public class TestUtil {public static final MediaType APPLICATION_JSON_UTF8 = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8") ); }

    獲取待辦事項(xiàng)條目

    我們必須測(cè)試的第二個(gè)控制器方法返回單個(gè)待辦事項(xiàng)的信息。 讓我們找出如何實(shí)現(xiàn)此控制器方法。

    預(yù)期行為

    通過執(zhí)行以下步驟來實(shí)現(xiàn)返回單個(gè)待辦事項(xiàng)信息的控制器方法:

  • 它處理發(fā)送到url'/ api / todo / {id}'的GET請(qǐng)求。 {id}是一個(gè)路徑變量,其中包含請(qǐng)求的待辦事項(xiàng)條目的ID 。
  • 它通過調(diào)用TodoService接口的findById()方法獲取請(qǐng)求的待辦事項(xiàng)條目,并將請(qǐng)求的待辦事項(xiàng)條目的ID作為方法參數(shù)傳遞。 此方法返回找到的待辦事項(xiàng)條目。 如果未找到待辦事項(xiàng)條目,則此方法將拋出TodoNotFoundException 。
  • 它將Todo對(duì)象轉(zhuǎn)換為TodoDTO對(duì)象。
  • 它返回創(chuàng)建的TodoDTO對(duì)象。
  • 我們的控制器方法的源代碼如下所示:

    import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*;@Controller public class TodoController {private TodoService service;@RequestMapping(value = "/api/todo/{id}", method = RequestMethod.GET)@ResponseBodypublic TodoDTO findById(@PathVariable("id") Long id) throws TodoNotFoundException {Todo found = service.findById(id);return createDTO(found);}private TodoDTO createDTO(Todo model) {TodoDTO dto = new TodoDTO();dto.setId(model.getId());dto.setDescription(model.getDescription());dto.setTitle(model.getTitle());return dto;} }

    返回給客戶端的JSON文檔如下所示:

    {"id":1,"description":"Lorem ipsum","title":"Foo" }

    我們的下一個(gè)問題是:

    拋出TodoNotFoundException會(huì)發(fā)生什么?

    我們的示例應(yīng)用程序具有一個(gè)異常處理程序類,該類處理由控制器類拋出的應(yīng)用程序特定的異常。 此類具有異常處理程序方法,當(dāng)拋出TodoNotFoundException時(shí)將調(diào)用該方法。 此方法的實(shí)現(xiàn)將新的日志消息寫入日志文件,并確保將HTTP狀態(tài)代碼404發(fā)送回客戶端。

    RestErrorHandler類的相關(guān)部分如下所示:

    import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus;@ControllerAdvice public class RestErrorHandler {private static final Logger LOGGER = LoggerFactory.getLogger(RestErrorHandler.class);@ExceptionHandler(TodoNotFoundException.class)@ResponseStatus(HttpStatus.NOT_FOUND)public void handleTodoNotFoundException(TodoNotFoundException ex) {LOGGER.debug("handling 404 error on a todo entry");} }

    我們必須為此控制器方法編寫兩個(gè)單元測(cè)試:

  • 我們必須編寫一個(gè)測(cè)試,以確保在未找到todo條目時(shí),我們的應(yīng)用程序能夠正常運(yùn)行。
  • 我們必須編寫一個(gè)測(cè)試,以在找到待辦事項(xiàng)條目時(shí)驗(yàn)證是否向客戶端返回了正確的數(shù)據(jù)。
  • 讓我們看看如何編寫這些測(cè)試。

    測(cè)試1:找不到待辦事項(xiàng)條目

    首先,當(dāng)找不到待辦事項(xiàng)時(shí),我們必須確保我們的應(yīng)用程序正常運(yùn)行。 我們可以按照以下步驟編寫一個(gè)單元測(cè)試來確保這一點(diǎn):

  • 將模擬對(duì)象配置為在調(diào)用其findById()方法且請(qǐng)求的待辦事項(xiàng)條目的ID為1L時(shí)引發(fā)TodoNotFoundException 。
  • 執(zhí)行GET請(qǐng)求以獲取url'/ api / todo / 1'。
  • 驗(yàn)證是否返回了HTTP狀態(tài)代碼404。
  • 確保使用正確的方法參數(shù)(1L)僅調(diào)用一次TodoService接口的findById()方法。
  • 驗(yàn)證在此測(cè)試期間沒有調(diào)用TodoService接口的其他方法。
  • 我們的單元測(cè)試的源代碼如下所示:

    import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc;import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {TestContext.class, WebAppContext.class}) @WebAppConfiguration public class TodoControllerTest {private MockMvc mockMvc;@Autowiredprivate TodoService todoServiceMock;//Add WebApplicationContext field here.//The setUp() method is omitted.@Testpublic void findById_TodoEntryNotFound_ShouldReturnHttpStatusCode404() throws Exception {when(todoServiceMock.findById(1L)).thenThrow(new TodoNotFoundException(""));mockMvc.perform(get("/api/todo/{id}", 1L)).andExpect(status().isNotFound());verify(todoServiceMock, times(1)).findById(1L);verifyNoMoreInteractions(todoServiceMock);} }

    測(cè)試2:找到Todo條目

    其次,我們必須編寫一個(gè)測(cè)試,以確保在找到請(qǐng)求的待辦事項(xiàng)條目時(shí)返回正確的數(shù)據(jù)。 我們可以按照以下步驟編寫測(cè)試來確保這一點(diǎn):

  • 創(chuàng)建Todo對(duì)象,該對(duì)象在調(diào)用我們的service方法時(shí)返回。 我們使用測(cè)試數(shù)據(jù)生成器創(chuàng)建此對(duì)象。
  • 配置我們的模擬對(duì)象以在使用方法參數(shù)1L調(diào)用其findById()方法時(shí)返回創(chuàng)建的Todo對(duì)象。
  • 執(zhí)行GET請(qǐng)求以獲取url'/ api / todo / 1'。
  • 驗(yàn)證是否返回HTTP狀態(tài)代碼200。
  • 確認(rèn)響應(yīng)的內(nèi)容類型為“ application / json”,其字符集為“ UTF-8”。
  • 通過使用JsonPath表達(dá)式$ .id獲取待辦事項(xiàng)的ID ,并驗(yàn)證ID為1。
  • 使用JsonPath表達(dá)式$ .description獲取待辦事項(xiàng)的描述 ,并驗(yàn)證該描述是否為“ Lorem ipsum”。
  • 通過使用JsonPath表達(dá)式$ .title獲取待辦事項(xiàng)的標(biāo)題 ,并驗(yàn)證標(biāo)題為“ Foo”。
  • 確保使用正確的方法參數(shù)(1L)僅調(diào)用一次TodoService接口的findById()方法。
  • 驗(yàn)證測(cè)試期間未調(diào)用模擬對(duì)象的其他方法。
  • 我們的單元測(cè)試的源代碼如下所示:

    import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc;import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {TestContext.class, WebAppContext.class}) @WebAppConfiguration public class TodoControllerTest {private MockMvc mockMvc;@Autowiredprivate TodoService todoServiceMock;//Add WebApplicationContext field here.//The setUp() method is omitted.@Testpublic void findById_TodoEntryFound_ShouldReturnFoundTodoEntry() throws Exception {Todo found = new TodoBuilder().id(1L).description("Lorem ipsum").title("Foo").build();when(todoServiceMock.findById(1L)).thenReturn(found);mockMvc.perform(get("/api/todo/{id}", 1L)).andExpect(status().isOk()).andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8)).andExpect(jsonPath("$.id", is(1))).andExpect(jsonPath("$.description", is("Lorem ipsum"))).andExpect(jsonPath("$.title", is("Foo")));verify(todoServiceMock, times(1)).findById(1L);verifyNoMoreInteractions(todoServiceMock);} }

    添加新的待辦事項(xiàng)

    第三種控制器方法將新的待辦事項(xiàng)條目添加到數(shù)據(jù)庫(kù),并返回添加的待辦事項(xiàng)條目的信息。 讓我們繼續(xù)前進(jìn),了解它是如何實(shí)現(xiàn)的。

    預(yù)期行為

    通過執(zhí)行以下步驟來實(shí)現(xiàn)向數(shù)據(jù)庫(kù)添加新的待辦事項(xiàng)條目的控制器方法:

  • 它處理發(fā)送到url'/ api / todo'的POST請(qǐng)求。
  • 它驗(yàn)證作為方法參數(shù)給出的TodoDTO對(duì)象。 如果驗(yàn)證失敗,則拋出MethodArgumentNotValidException 。
  • 它通過調(diào)用TodoService接口的add()方法向數(shù)據(jù)庫(kù)添加一個(gè)新的todo條目,并將TodoDTO對(duì)象作為方法參數(shù)傳遞。 此方法將新的待辦事項(xiàng)添加到數(shù)據(jù)庫(kù),并返回添加的待辦事項(xiàng)。
  • 它將創(chuàng)建的Todo對(duì)象轉(zhuǎn)換為TodoDTO對(duì)象。
  • 它返回TodoDTO對(duì)象。
  • 我們的控制器方法的源代碼如下所示:

    import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*;import javax.validation.Valid;@Controller public class TodoController {private TodoService service;@RequestMapping(value = "/api/todo", method = RequestMethod.POST)@ResponseBodypublic TodoDTO add(@Valid @RequestBody TodoDTO dto) {Todo added = service.add(dto);return createDTO(added);}private TodoDTO createDTO(Todo model) {TodoDTO dto = new TodoDTO();dto.setId(model.getId());dto.setDescription(model.getDescription());dto.setTitle(model.getTitle());return dto;} }

    TodoDTO類是一個(gè)簡(jiǎn)單的DTO類,其源代碼如下所示:

    import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.NotEmpty;public class TodoDTO {private Long id;@Length(max = 500)private String description;@NotEmpty@Length(max = 100)private String title;//Constructor and other methods are omitted. }

    如我們所見,該類聲明了以下三個(gè)約束條件:

  • 的描述的最大長(zhǎng)度為500個(gè)字符。
  • 待辦事項(xiàng)的標(biāo)題不能為空。
  • 標(biāo)題的最大長(zhǎng)度為100個(gè)字符。
  • 如果驗(yàn)證失敗,我們的錯(cuò)誤處理程序組件將確保

  • HTTP狀態(tài)代碼400返回到客戶端。
  • 驗(yàn)證錯(cuò)誤將作為JSON文檔返回給客戶端。
  • 因?yàn)槲乙呀?jīng)寫了一篇博客文章 ,描述了我們?nèi)绾蜗騌EST API添加驗(yàn)證,所以本文中沒有討論錯(cuò)誤處理程序組件的實(shí)現(xiàn)。

    但是,如果驗(yàn)證失敗,我們需要知道將哪種JSON文檔返回給客戶端。 該信息在下面給出。

    如果TodoDTO對(duì)象的標(biāo)題和描述太長(zhǎng),則會(huì)將以下JSON文檔返回給客戶端:

    {"fieldErrors":[{"path":"description","message":"The maximum length of the description is 500 characters."},{"path":"title","message":"The maximum length of the title is 100 characters."}] }

    注意 :Spring MVC不保證字段錯(cuò)誤的順序。 換句話說,場(chǎng)錯(cuò)誤以隨機(jī)順序返回。 在為該控制器方法編寫單元測(cè)試時(shí),必須考慮到這一點(diǎn)。

    另一方面,如果驗(yàn)證沒有失敗,那么我們的控制器方法將以下JSON文檔返回給客戶端:

    {"id":1,"description":"description","title":"todo" }

    我們必須為此控制器方法編寫兩個(gè)單元測(cè)試:

  • 我們必須編寫一個(gè)測(cè)試,以確保驗(yàn)證失敗時(shí)我們的應(yīng)用程序能夠正常運(yùn)行。
  • 我們必須編寫一個(gè)測(cè)試,以確保在將新的待辦事項(xiàng)添加到數(shù)據(jù)庫(kù)時(shí),我們的應(yīng)用程序能夠正常運(yùn)行。
  • 讓我們找出如何編寫這些測(cè)試。

    測(cè)試1:驗(yàn)證失敗

    我們的第一個(gè)測(cè)試確保當(dāng)添加的todo條目的驗(yàn)證失敗時(shí),我們的應(yīng)用程序可以正常運(yùn)行。 我們可以按照以下步驟編寫此測(cè)試:

  • 創(chuàng)建一個(gè)具有101個(gè)字符的標(biāo)題 。
  • 創(chuàng)建一個(gè)包含501個(gè)字符的描述 。
  • 使用我們的測(cè)試數(shù)據(jù)構(gòu)建器創(chuàng)建一個(gè)新的TodoDTO對(duì)象。 設(shè)置對(duì)象的標(biāo)題和描述 。
  • 執(zhí)行POST請(qǐng)求以發(fā)送url'/ api / todo'。 將請(qǐng)求的內(nèi)容類型設(shè)置為“ application / json”。 將請(qǐng)求的字符集設(shè)置為“ UTF-8”。 將創(chuàng)建的TodoDTO對(duì)象轉(zhuǎn)換為JSON字節(jié)并將其發(fā)送到請(qǐng)求的正文中。
  • 驗(yàn)證是否返回了HTTP狀態(tài)代碼400。
  • 驗(yàn)證響應(yīng)的內(nèi)容類型為“ application / json”,并且其內(nèi)容類型為“ UTF-8”。
  • 通過使用JsonPath表達(dá)式$ .fieldErrors提取字段錯(cuò)誤,并確保返回兩個(gè)字段錯(cuò)誤。
  • 通過使用JsonPath表達(dá)式$ .fieldErrors [*]。path獲取所有可用路徑,并確保找到有關(guān)title和description字段的字段錯(cuò)誤。
  • 通過使用JsonPath表達(dá)式$ .fieldErrors [*]。message來獲取所有可用的錯(cuò)誤消息,并確保找到有關(guān)標(biāo)題和描述字段的錯(cuò)誤消息。
  • 驗(yàn)證測(cè)試期間未調(diào)用模擬對(duì)象的方法。
  • 我們的單元測(cè)試的源代碼如下所示:

    import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc;import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.hasSize; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {TestContext.class, WebAppContext.class}) @WebAppConfiguration public class TodoControllerTest {private MockMvc mockMvc;@Autowiredprivate TodoService todoServiceMock;//Add WebApplicationContext field here.//The setUp() method is omitted.@Testpublic void add_TitleAndDescriptionAreTooLong_ShouldReturnValidationErrorsForTitleAndDescription() throws Exception {String title = TestUtil.createStringWithLength(101);String description = TestUtil.createStringWithLength(501);TodoDTO dto = new TodoDTOBuilder().description(description).title(title).build();mockMvc.perform(post("/api/todo").contentType(TestUtil.APPLICATION_JSON_UTF8).content(TestUtil.convertObjectToJsonBytes(dto))).andExpect(status().isBadRequest()).andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8)).andExpect(jsonPath("$.fieldErrors", hasSize(2))).andExpect(jsonPath("$.fieldErrors[*].path", containsInAnyOrder("title", "description"))).andExpect(jsonPath("$.fieldErrors[*].message", containsInAnyOrder("The maximum length of the description is 500 characters.","The maximum length of the title is 100 characters.")));verifyZeroInteractions(todoServiceMock);} }

    我們的單元測(cè)試使用TestUtil類的兩個(gè)靜態(tài)方法。 下面介紹了這些方法:

    • createStringWithLength(int length)方法使用給定的長(zhǎng)度創(chuàng)建一個(gè)新的String對(duì)象,并返回創(chuàng)建的對(duì)象。
    • convertObjectToJsonBytes(Object object)方法將作為方法參數(shù)給出的對(duì)象轉(zhuǎn)換為JSON文檔,并將該文檔的內(nèi)容作為字節(jié)數(shù)組返回 。

    TestUtil類的源代碼如下所示:

    import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.http.MediaType;import java.io.IOException; import java.nio.charset.Charset;public class TestUtil {public static final MediaType APPLICATION_JSON_UTF8 = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));public static byte[] convertObjectToJsonBytes(Object object) throws IOException {ObjectMapper mapper = new ObjectMapper();mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);return mapper.writeValueAsBytes(object);}public static String createStringWithLength(int length) {StringBuilder builder = new StringBuilder();for (int index = 0; index < length; index++) {builder.append("a");}return builder.toString();} }

    測(cè)試2:Todo條目已添加到數(shù)據(jù)庫(kù)

    第二個(gè)單元測(cè)試確保將新的待辦事項(xiàng)添加到數(shù)據(jù)庫(kù)時(shí),控制器能夠正常工作。 我們可以按照以下步驟編寫此測(cè)試:

  • 使用我們的測(cè)試數(shù)據(jù)構(gòu)建器創(chuàng)建一個(gè)新的TodoDTO對(duì)象。 在標(biāo)題和說明字段中設(shè)置“合法”值。
  • 創(chuàng)建一個(gè)Todo對(duì)象,該對(duì)象在調(diào)用TodoService接口的add()方法時(shí)返回。
  • 配置我們的模擬對(duì)象,使其在調(diào)用其add()方法并將TodoDTO對(duì)象作為參數(shù)時(shí)返回創(chuàng)建的Todo對(duì)象。
  • 執(zhí)行POST請(qǐng)求以發(fā)送url'/ api / todo'。 將請(qǐng)求的內(nèi)容類型設(shè)置為“ application / json”。 將請(qǐng)求的字符集設(shè)置為“ UTF-8”。 將創(chuàng)建的TodoDTO對(duì)象轉(zhuǎn)換為JSON字節(jié)并將其發(fā)送到請(qǐng)求的正文中。
  • 驗(yàn)證是否返回HTTP狀態(tài)代碼200。
  • 驗(yàn)證響應(yīng)的內(nèi)容類型為“ application / json”,并且其內(nèi)容類型為“ UTF-8”。
  • 使用JsonPath表達(dá)式$ .id獲取返回的待辦事項(xiàng)條目的ID ,并驗(yàn)證ID為1。
  • 使用JsonPath表達(dá)式$ .description獲取返回的待辦事項(xiàng)的描述 ,并驗(yàn)證該描述是否為“描述”。
  • 通過使用JsonPath表達(dá)式$ .title獲取返回的待辦事項(xiàng)條目的標(biāo)題 ,并確保標(biāo)題為“ title”。
  • 創(chuàng)建一個(gè)ArgumentCaptor對(duì)象,該對(duì)象可以捕獲TodoDTO對(duì)象。
  • 驗(yàn)證TodoService接口的add()方法僅被調(diào)用一次,并捕獲作為參數(shù)給定的對(duì)象。
  • 驗(yàn)證測(cè)試期間未調(diào)用模擬對(duì)象的其他方法。
  • 驗(yàn)證捕獲的TodoDTO對(duì)象的ID為null。
  • 驗(yàn)證所捕獲的TodoDTO對(duì)象的描述是“說明”。
  • 驗(yàn)證捕獲TodoDTO對(duì)象的標(biāo)題是“冠軍”。
  • 我們的單元測(cè)試的源代碼如下所示:

    import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc;import static junit.framework.Assert.assertNull; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {TestContext.class, WebAppContext.class}) @WebAppConfiguration public class TodoControllerTest {private MockMvc mockMvc;@Autowiredprivate TodoService todoServiceMock;//Add WebApplicationContext field here.//The setUp() method is omitted.@Testpublic void add_NewTodoEntry_ShouldAddTodoEntryAndReturnAddedEntry() throws Exception {TodoDTO dto = new TodoDTOBuilder().description("description").title("title").build();Todo added = new TodoBuilder().id(1L).description("description").title("title").build();when(todoServiceMock.add(any(TodoDTO.class))).thenReturn(added);mockMvc.perform(post("/api/todo").contentType(TestUtil.APPLICATION_JSON_UTF8).content(TestUtil.convertObjectToJsonBytes(dto))).andExpect(status().isOk()).andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8)).andExpect(jsonPath("$.id", is(1))).andExpect(jsonPath("$.description", is("description"))).andExpect(jsonPath("$.title", is("title")));ArgumentCaptor<TodoDTO> dtoCaptor = ArgumentCaptor.forClass(TodoDTO.class);verify(todoServiceMock, times(1)).add(dtoCaptor.capture());verifyNoMoreInteractions(todoServiceMock);TodoDTO dtoArgument = dtoCaptor.getValue();assertNull(dtoArgument.getId());assertThat(dtoArgument.getDescription(), is("description"));assertThat(dtoArgument.getTitle(), is("title"));} }

    摘要

    現(xiàn)在,我們已經(jīng)使用Spring MVC Test框架為REST API編寫了單元測(cè)試。 本教程教會(huì)了我們四件事:

    • 我們學(xué)習(xí)了為控制器方法編寫單元測(cè)試,這些方法從數(shù)據(jù)庫(kù)中讀取信息。
    • 我們學(xué)習(xí)了為控制器方法編寫單元測(cè)試,這些方法將信息添加到數(shù)據(jù)庫(kù)中。
    • 我們了解了如何將DTO對(duì)象轉(zhuǎn)換為JSON字節(jié)并將轉(zhuǎn)換結(jié)果發(fā)送到請(qǐng)求正文中。
    • 我們學(xué)習(xí)了如何使用JsonPath表達(dá)式編寫JSON文檔的斷言。

    與往常一樣,此博客文章的示例應(yīng)用程序可在Github上獲得 。 我建議您檢查一下,因?yàn)樗泻芏鄦卧獪y(cè)試,而本博客文章中未涉及。

    參考: Spring MVC控制器的單元測(cè)試: Petri Kainulainen博客上的JCG合作伙伴 Petri Kainulainen提供的REST API 。

    翻譯自: https://www.javacodegeeks.com/2013/08/unit-testing-of-spring-mvc-controllers-rest-api.html

    總結(jié)

    以上是生活随笔為你收集整理的Spring MVC控制器的单元测试:REST API的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。