공부/Spring Boot
Spring REST Docs(5) - 문서조각 커스텀
오피스엑소더스
2020. 12. 28. 17:40
request-fields에 required 또는 optional 컬럼을 추가하고싶다면?
문서조각 템플릿을 커스텀 할 수 있다.
아래 경로에 커스텀하고싶은 템플릿 생성
경로 : src/test/resources/org/springframework/restdocs/templates
주의(!) 평소 패키지 추가하던것 처럼 ~/org.spring~.~.templates 이런식으로 온점으로 구분하면 안된다 경로기 때문에
테스트코드
여기서는 fieldWithPath 뒤에 optional()을 붙여줬다
package com.exam.restdocs.market;
import com.exam.restdocs.common.RestDocsConfiguration;
import com.exam.restdocs.domain.Employee;
import com.exam.restdocs.domain.Item;
import com.exam.restdocs.domain.Market;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.FileUtils;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import java.io.File;
import java.util.LinkedHashSet;
import static org.springframework.restdocs.headers.HeaderDocumentation.*;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.*;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.head;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureRestDocs
@Import(RestDocsConfiguration.class)
public class MarketTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
// @Ignore
@Test
public void createMarket() throws Exception{
String jsonData = FileUtils.readFileToString(new File("src/test/resources/market_1.json"));
mockMvc
.perform(
post("/market/create")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(jsonData))
//.content(objectMapper.writeValueAsString(market))//objectMapper가 json string으로 바꿔줌
.andDo(print())
.andDo(document("create-market",
links(
halLinks(),
linkWithRel("self").description("link to self")
),
requestHeaders(
headerWithName(HttpHeaders.ACCEPT).description("accept header"),
headerWithName(HttpHeaders.CONTENT_TYPE).description("header content type")
),
relaxedRequestFields(
fieldWithPath("marketName").description("Name"),
fieldWithPath("location").description("desc")
),
responseHeaders(
headerWithName(HttpHeaders.LOCATION).description("resp header location")
)
// ,
// responseFields(
// fieldWithPath("respName").description("resp name"),
// subsectionWithPath("respSub").description("sub section")
// )
)
);
}
@Test
public void createMarketEntity() throws Exception{
String body = FileUtils.readFileToString(new File("src/test/resources/market_1.json"));
mockMvc.perform(post("/market/create/entity")
.contentType(MediaType.APPLICATION_JSON)//요청 타입은 JSON이다
.accept(MediaTypes.HAL_JSON)//HAL JSON을 돌려달라
.content(body)
)
.andDo(print())
.andExpect(status().isCreated())
.andExpect(jsonPath("market.id").exists())//id가 있는지 확인
.andExpect(jsonPath("_links.self").exists())
.andExpect(jsonPath("_links.markets").exists())
.andExpect(jsonPath("_links.update-market").exists())
//적용한 스니펫 : links
.andDo(document("create-market",
links(//링크정보
linkWithRel("self").description("link to self"),
linkWithRel("markets").description("link to show all markets"),
linkWithRel("update-market").description("link to update a market"),
linkWithRel("profile").description("link to update a market")
),
requestHeaders(//요청 헤더
headerWithName(HttpHeaders.ACCEPT).description("accept type"),
headerWithName(HttpHeaders.CONTENT_TYPE).description("content type")
),
requestFields(//요청 필드
fieldWithPath("marketName").description("name of new market").optional(),
fieldWithPath("location").description("location of market"),
fieldWithPath("employees.[].name").description("name of employee of market"),
fieldWithPath("employees.[].age").description("name of employee of market"),
fieldWithPath("items.[].name").description("name of item of market"),
fieldWithPath("items.[].category").description("category of item of market"),
fieldWithPath("items.[].quantity").description("quantity of item of market")
),
responseHeaders(
headerWithName(HttpHeaders.LOCATION).description("accept type"),
headerWithName(HttpHeaders.CONTENT_TYPE).description("HAL JSON type")
),
//responseFields(
relaxedResponseFields(//relaxed : 문서의 일부분만 확인해도 되게끔 설정해주는 prefix
//fieldWithPath : 응답의 필드를 기술하기 위한 메소드
//subsectioniiWithPath : 하위섹션에 대한 정보를 기술하기 위한 메소드
subsectionWithPath("market").description("info of market"),
fieldWithPath("market.id").description("id of market"),
fieldWithPath("market.marketName").description("name of new market"),
fieldWithPath("market.location").description("location of market"),
subsectionWithPath("market.employees[]").description("employees of market"),
fieldWithPath("market.employees[].id").description("id of employee of market"),
fieldWithPath("market.employees.[].name").description("name of employee of market"),
fieldWithPath("market.employees.[].age").description("name of employee of market"),
fieldWithPath("market.employees.[].market").description("market of employee of market"),
subsectionWithPath("market.items").description("items of market"),
fieldWithPath("market.items.[].id").description("id of item of market"),
fieldWithPath("market.items.[].name").description("name of item of market"),
fieldWithPath("market.items.[].category").description("category of item of market"),
fieldWithPath("market.items.[].quantity").description("quantity of item of market"),
fieldWithPath("market.items.[].market").description("market of item of market"),
fieldWithPath("_links.profile").description("profile")
)
)
)
;
}
}
//request-fileds.snippet
===== Request Fields
|===
|필드명|타입|필수값|설명
{{#fields}}
|{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}}
|{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}}
|{{#tableCellContent}}{{^optional}}true{{/optional}}{{/tableCellContent}} //(2)
|{{#tableCellContent}}{{description}}{{/tableCellContent}}
{{/fields}}
|===
다른방법(spring restdocs 공식문서)
위 예제 처럼 optional() 메서드를 이용하지 않고 attributes() 메서드 활용
.andDo(document("create-user",
requestFields(
fieldWithPath("name").description("The user's name")
.attributes(key("constraints").value("Must not be null. Must not be empty")),
fieldWithPath("email").description("The user's email address")
.attributes(key("constraints").value("Must be a valid email address")))));
//request-fileds.snippet
.{{title}}
|===
|Path|Type|Description|Constraints
{{#fields}}
|{{path}}
|{{type}}
|{{description}}
|{{constraints}}
{{/fields}}
|===
참고 : woowabros.github.io/experience/2018/12/28/spring-rest-docs.html