작성
·
156
·
수정됨
0
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!
먼저 유사한 질문이 있었는지 검색해 보세요.
서로 예의를 지키며 존중하는 문화를 만들어가요.
안녕하세요. 강의 너무 잘 듣고 있습니다! 현재까지 진행해온 프로젝트의 OrderController에도 Spring Rest Docs를 적용해보는 와중에 몇가지 질문이 생겼습니다!
public class OrderControllerDocsTest extends RestDocsSupport {
private final OrderService orderService = mock(OrderService.class);
private static List<ProductResponse> productResponses;
@Override
protected Object initController() {
return new OrderController(orderService);
}
@BeforeEach
void init() throws Exception {
ProductResponse productResponse1 = ProductResponse.builder()
.id(1L)
.price(1000)
.sellingStatus(SELLING)
.productNumber("001")
.type(HANDMADE)
.name("상품명1")
.build();
ProductResponse productResponse2 = ProductResponse.builder()
.id(2L)
.price(3000)
.sellingStatus(SELLING)
.productNumber("002")
.type(HANDMADE)
.name("상품명2")
.build();
productResponses = List.of(productResponse1, productResponse2);
}
@DisplayName("새로운 주문을 생성한다.")
@Test
void createOrder() throws Exception {
//given
LocalDateTime now = LocalDateTime.of(2025, 1, 28, 0, 50);
List<String> productNumbers = List.of("001", "002");
OrderCreateRequest request = OrderCreateRequest.builder()
.productNumbers(productNumbers)
.build();
int totalPrice = productResponses.stream().mapToInt(ProductResponse::getPrice).sum();
OrderResponse orderResponse = OrderResponse.builder()
.id(1L)
.registeredDateTime(now)
.products(productResponses)
.totalPrice(totalPrice)
.build();
given(orderService.createOrder(any(OrderCreateServiceRequest.class), any(LocalDateTime.class)))
.willReturn(orderResponse);
//when
mockMvc.perform(post("/api/v1/orders/new")
.content(mapper.writeValueAsString(request))
.contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
.andDo(document("order-create",
preprocessRequest(prettyPrint()), // adoc에서 json 형태를 보기 좋게 만들어줌
preprocessResponse(prettyPrint()),
requestFields(
fieldWithPath("productNumbers").type(JsonFieldType.ARRAY)
.description("상품 리스트")
),
responseFields(
fieldWithPath("code").type(JsonFieldType.NUMBER)
.description("코드"),
fieldWithPath("status").type(JsonFieldType.STRING)
.description("상태"),
fieldWithPath("message").type(JsonFieldType.STRING)
.description("메시지"),
fieldWithPath("data").type(JsonFieldType.OBJECT)
.description("응답 데이터"),
fieldWithPath("data.id").type(JsonFieldType.NUMBER)
.description("주문 ID"),
fieldWithPath("data.totalPrice").type(JsonFieldType.NUMBER)
.description("총 주문 가격"),
fieldWithPath("data.registeredDateTime").type(JsonFieldType.ARRAY) // 반환타입 ARRAY ?
.description("주문 시간"),
fieldWithPath("data.products").type(JsonFieldType.ARRAY)
.description("주문 상품 리스트"),
fieldWithPath("data.products[].id").type(JsonFieldType.NUMBER)
.description("주문 상품 ID"),
fieldWithPath("data.products[].productNumber").type(JsonFieldType.STRING)
.description("주문 상품 번호"),
fieldWithPath("data.products[].type").type(JsonFieldType.STRING)
.description("주문 상품 타입"),
fieldWithPath("data.products[].sellingStatus").type(JsonFieldType.STRING)
.description("주문 상품 상태"),
fieldWithPath("data.products[].name").type(JsonFieldType.STRING)
.description("주문 상품명"),
fieldWithPath("data.products[].price").type(JsonFieldType.NUMBER)
.description("주문 상품 가격")
))
);
}
}
OrderService.createOrder() 메서드 반환 값 OrderResponse를 생성하기 위해선 ProductResponse 객체가 필요합니다. 이러한 경우가 강사님이 말씀하신 @BeforeEach의 적용시점인가? 라는 생각이 들어 위와 같이 작성해봤습니다. 제가 이해한 바가 맞을까요??
requestFields(
fieldWithPath("productNumbers").type(JsonFieldType.ARRAY)
.description("상품 리스트")
),
위 코드에서 productNumbers에 있는 각 원소의 타입이 String 인것을 API 문서에 나타내고 싶어 fieldWithPath("productNumbers[]").type(JsonFieldType.STRING
과 같이 작성해봤지만 타입 미스매칭 오류가 발생했습니다. responseFields에는 동일한 방법으로 List 순회에 성공했지만 requestFields 순회 문제는 해결하지 못했습니다.
혹시 방법이 있다면 알려주시면 감사하겠습니다!
답변 2
0
안녕하세요, 도토리묵 님!
1. OrderService.createOrder() 메서드 반환 값 OrderResponse를 생성하기 위해선 ProductResponse 객체가 필요합니다. 이러한 경우가 강사님이 말씀하신 @BeforeEach의 적용시점인가? 라는 생각이 들어 위와 같이 작성해봤습니다. 제가 이해한 바가 맞을까요??
네, 맞습니다. 문서 테스트이긴 하지만 Order에 관련된 테스트이니 부차적인 Product의 항목은 BeforeEach 단계에서 생성하여 가독성을 높이고, 각 테스트 케이스가 공유하도록 할 수 있겠네요.
2. 위 코드에서 productNumbers에 있는 각 원소의 타입이 String 인것을 API 문서에 나타내고 싶어
fieldWithPath("productNumbers[]").type(JsonFieldType.STRING
과 같이 작성해봤지만 타입 미스매칭 오류가 발생했습니다. responseFields에는 동일한 방법으로 List 순회에 성공했지만 requestFields 순회 문제는 해결하지 못했습니다.
혹시 방법이 있다면 알려주시면 감사하겠습니다!
저도 그렇게 명시해본 적은 없어서 찾아봤는데, 특별한 방법이 있는 것 같지는 않네요 🥲
기본적으로 ARRAY 타입이면 String 배열임을 드러낼 수 있어서, 추가적인 명시를 원한다면 다음과 같은 방법을 사용하면 될 것 같아요.
요청 예시로 자연스럽게 표현
description에 예시와 함께 상세 기재
도움이 되셨기를 바랍니다.
감사합니다. 🙂
0
안녕하세요, 인프런 AI 인턴입니다. 도토리묵님!
Spring REST Docs 작성과 관련하여 몇 가지 고민하고 계시는 것 같아 도움을 드리고자 합니다.
@BeforeEach
활용: @BeforeEach
애너테이션은 각 테스트가 실행되기 전에 반복해서 초기 설정을 하고자 할 때 유용합니다. 질문하신 코드에서 @BeforeEach
를 사용하여 ProductResponse
객체들을 초기화하고, 테스트 메서드에서 재사용하는 방식은 효율적입니다. 이 방식은 중복 코드를 줄일 수 있어 여러 테스트에서 동일한 초기 설정이 필요할 때 적절합니다.
requestFields
에서 배열의 원소 타입 지정 문제: REST Docs에서 requestFields
는 전체 요청 본문(request body)을 대상으로 필드의 구조를 설명합니다. fieldWithPath("productNumbers[]").type(JsonFieldType.STRING)
와 같은 표현은 일치하지 않는 이유가 productNumbers
는 배열(리스트)이며, JsonFieldType.ARRAY
로 선언해야 하기 때문입니다. 각 원소의 타입을 문서에 명시적으로 표현하는 것은 REST Docs에서 직접적으로 제공하진 않지만, 일반적으로 필드에 대해 추가 설명을 덧붙여 해소할 수 있습니다. 예를 들면 description("상품 리스트 (문자열 IDS)")
와 같이 정보를 제공할 수 있습니다.
관련된 기존 질문과 답변을 참고하시면서 문제를 해결해보시기를 추천드립니다.
본 답변이 도토리묵님의 이해를 돕고, 문제 해결에 도움이 되길 바랍니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.