inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

Practical Testing: 실용적인 테스트 가이드

Spring REST Docs

Spring Rest Docs request에 List 타입이 포함된 경우

182

도토리묵

작성한 질문수 5

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("주문 상품 가격")

                        ))
                );
    }
}
  1. OrderService.createOrder() 메서드 반환 값 OrderResponse를 생성하기 위해선 ProductResponse 객체가 필요합니다. 이러한 경우가 강사님이 말씀하신 @BeforeEach의 적용시점인가? 라는 생각이 들어 위와 같이 작성해봤습니다. 제가 이해한 바가 맞을까요??

  2.  

    requestFields(
                                    fieldWithPath("productNumbers").type(JsonFieldType.ARRAY)
                                            .description("상품 리스트")
                            ),

    위 코드에서 productNumbers에 있는 각 원소의 타입이 String 인것을 API 문서에 나타내고 싶어 fieldWithPath("productNumbers[]").type(JsonFieldType.STRING과 같이 작성해봤지만 타입 미스매칭 오류가 발생했습니다. responseFields에는 동일한 방법으로 List 순회에 성공했지만 requestFields 순회 문제는 해결하지 못했습니다.
    혹시 방법이 있다면 알려주시면 감사하겠습니다!

spring tdd jpa mockito 소프트웨어-테스트 junit5

답변 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 인턴

안녕하세요, 인프런 AI 인턴입니다. 도토리묵님!

Spring REST Docs 작성과 관련하여 몇 가지 고민하고 계시는 것 같아 도움을 드리고자 합니다.

  1. @BeforeEach 활용: @BeforeEach 애너테이션은 각 테스트가 실행되기 전에 반복해서 초기 설정을 하고자 할 때 유용합니다. 질문하신 코드에서 @BeforeEach를 사용하여 ProductResponse 객체들을 초기화하고, 테스트 메서드에서 재사용하는 방식은 효율적입니다. 이 방식은 중복 코드를 줄일 수 있어 여러 테스트에서 동일한 초기 설정이 필요할 때 적절합니다.

  2. requestFields에서 배열의 원소 타입 지정 문제: REST Docs에서 requestFields는 전체 요청 본문(request body)을 대상으로 필드의 구조를 설명합니다. fieldWithPath("productNumbers[]").type(JsonFieldType.STRING)와 같은 표현은 일치하지 않는 이유가 productNumbers는 배열(리스트)이며, JsonFieldType.ARRAY로 선언해야 하기 때문입니다. 각 원소의 타입을 문서에 명시적으로 표현하는 것은 REST Docs에서 직접적으로 제공하진 않지만, 일반적으로 필드에 대해 추가 설명을 덧붙여 해소할 수 있습니다. 예를 들면 description("상품 리스트 (문자열 IDS)")와 같이 정보를 제공할 수 있습니다.

관련된 기존 질문과 답변을 참고하시면서 문제를 해결해보시기를 추천드립니다.

본 답변이 도토리묵님의 이해를 돕고, 문제 해결에 도움이 되길 바랍니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

private 상수 테스트 관련 질문

0

82

1

void는 어떻게 테스트하나요..? void로 애초에 코딩하면 안되나요??

0

124

2

커버리지는 어떻게 활용하시는지 궁금합니다.

0

159

2

테스트 문서화 질문입니다

0

104

2

단위테스트 질문이 있습니다

0

94

2

컨트롤러는 모킹을 한 이유가 궁금합니다.

0

100

2

ERD 가장자리에 있는 도메인 테스트 질문

0

86

2

DTO 검증 필드에 대한 테스트 코드 작성은 어디까지?

0

131

2

OrderCreateRequest DTO에 대해서 궁금한점

0

101

2

고전파의 테스트 대역 사용 대상, 공유 의존성

0

154

2

계층 관련 질문이 있습니다.

0

137

3

'코틀린'에서는 빌더를 따로 쓰지 않는데, 이 때는 어떻게 test fixture를 만드시는지 궁금합니다

1

122

2

혹시 update 로직은 어떻게 테스트하나요? (@Setter?)

0

133

2

단위테스트와 통합테스트의 경계가 궁금합니다.

0

226

2

Service+Repository 통합테스트 관련 질문입니다.

0

149

2

OrderControllerDocsTest 작성 해봤는데요. 날짜 형식이 이상하게 나와요

0

183

2

test 용 .yml

0

89

2

throws Exception

0

78

2

카페키오스크 클래스 문의 ,,

0

87

2

Rest docs 문서용 테스트코드를 따로 작성해야 되나요?

0

171

2

테스트 코드에서 필요한 생성자

0

136

1

tearDown 순서

0

114

2

@Builder 생성자 private

0

133

2

@DisplayName gradle / intellJ

0

91

2