104DAYS) [Main-Project] Review에 hairShopId 추가, findReviews 변경, 테스트 코드 변경, API 명세화

nacSeo (낙서)·2023년 3월 17일
0

어제 저녁 디렉토리 구조를 리팩토링하고 합쳐본 뒤 BE 회의 결과 reviewLike보다 hairShopLike가 프로젝트 요구사항에 걸맞다고 판단하여 리팩토링을 진행했다. 따로 로직에 큰 변화가 있었던 것은 아니고 hairShopLike로 명을 바꾸고 review쪽이 아니라 hairShop쪽과 연관 관계를 바꿔주었다.

추가로 이후 CORS 에러가 안나게 CrossOrigin 어노테이션을 추가해주고 멘토분께서 import에 와일드카드(*) 대신 무엇을 임포트하였는지 명시하는 방향이 좋다고 피드백을 주셔서 적용시켰다.

review쪽에도 hairShopId를 추가해주고 finReviews에 이를 이용한 로직을 추가해주었다.

마지막으로 디렉토리 구조에 따라 reviewController 테스트 코드를 짜고 Spring Restdocs를 이용하여 API 명세 1차 버전을 만들었다.

reviewControllerRestDocsTest

@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureRestDocs
public class ReviewControllerRestDocsTest {
    @Autowired
    private MockMvc mockMvc;
    @Autowired
    private Gson gson;
    @MockBean
    private ReviewCompositeService compositeService;
    @MockBean
    private ReviewMapper mapper;

    @Test
    void postReviewTest() throws Exception {
        // given
        Principal principal = () -> "test@test.com";

        ReviewDto.Post post = new ReviewDto.Post("이미지", "텍스트", null);
        String content = gson.toJson(post);

        ReviewDto.Response responseDto = new ReviewDto.Response(1L, "이미지", "텍스트",
                LocalDateTime.now(), LocalDateTime.now());

        given(mapper.reviewPostDtoToReview(Mockito.any(ReviewDto.Post.class))).willReturn(new Review());

        given(compositeService.createReview(Mockito.any(Review.class), Mockito.anyString())).willReturn(new Review());

        given(mapper.reviewToReviewResponseDto(Mockito.any(Review.class))).willReturn(responseDto);

        // when
        ResultActions actions =
                mockMvc.perform(
                        post("/reviews")
                                .accept(MediaType.APPLICATION_JSON)
                                .contentType(MediaType.APPLICATION_JSON)
                                .content(content)
                                .with(user("test@test.com"))
                );

        // then
        actions
                .andExpect(status().isCreated())
                .andExpect(header().string("Location", is(startsWith("/reviews"))))
                .andDo(document(
                        "post-review",
                        getRequestPreProcessor(),
                        getResponsePreProcessor(),
                        requestFields(
                                List.of(
                                        fieldWithPath("reviewImage").type(JsonFieldType.STRING).description("리뷰 이미지"),
                                        fieldWithPath("reviewText").type(JsonFieldType.STRING).description("리뷰 텍스트")
                                )
                        ),
                        responseHeaders(
                                headerWithName(HttpHeaders.LOCATION).description("Location header. 등록된 리소스의 URI")
                        ),
                        responseFields(
                                List.of(
                                        fieldWithPath("id").type(JsonFieldType.NUMBER).description("리뷰 식별자"),
                                        fieldWithPath("reviewImage").type(JsonFieldType.STRING).description("리뷰 이미지"),
                                        fieldWithPath("reviewText").type(JsonFieldType.STRING).description("리뷰 텍스트"),
                                        fieldWithPath("createdAt").type(JsonFieldType.STRING).description("리뷰 생성 시간"),
                                        fieldWithPath("modifiedAt").type(JsonFieldType.STRING).description("리뷰 수정 시간")
                                )
                        )
                ));
    }

    @Test
    void patchReviewTest() throws Exception {
        // given
        Long reviewId = 1L;

        Principal principal = () -> "test@test.com";

        ReviewDto.Patch patch = new ReviewDto.Patch(1L, "이미지", "텍스트", null);
        String content = gson.toJson(patch);

        ReviewDto.Response responseDto = new ReviewDto.Response(1L, "이미지", "텍스트",
                LocalDateTime.now(), LocalDateTime.now());

        given(mapper.reviewPatchDtoToReview(Mockito.any(ReviewDto.Patch.class))).willReturn(new Review());

        given(compositeService.updateReview(Mockito.any(Review.class), Mockito.anyString())).willReturn(new Review());

        given(mapper.reviewToReviewResponseDto(Mockito.any(Review.class))).willReturn(responseDto);

        // when
        ResultActions actions =
                mockMvc.perform(
                        RestDocumentationRequestBuilders
                                .patch("/reviews/{review-id}", reviewId)
                                .accept(MediaType.APPLICATION_JSON)
                                .contentType(MediaType.APPLICATION_JSON)
                                .content(content)
                                .with(user("test@test.com"))
                );

        // then
        actions
                .andExpect(status().isOk())
                .andDo(document(
                        "patch-review",
                        getRequestPreProcessor(),
                        getResponsePreProcessor(),
                        pathParameters(
                                parameterWithName("review-id").description("리뷰 식별자 ID")
                        ),
                        requestFields(
                                List.of(
                                        fieldWithPath("id").type(JsonFieldType.NUMBER).description("아이디").ignored(),
                                        fieldWithPath("reviewImage").type(JsonFieldType.STRING).description("리뷰 이미지").optional(),
                                        fieldWithPath("reviewText").type(JsonFieldType.STRING).description("리뷰 텍스트").optional()
                                )
                        ),
                        responseFields(
                                List.of(
                                        fieldWithPath("id").type(JsonFieldType.NUMBER).description("리뷰 식별자"),
                                        fieldWithPath("reviewImage").type(JsonFieldType.STRING).description("리뷰 이미지"),
                                        fieldWithPath("reviewText").type(JsonFieldType.STRING).description("리뷰 텍스트"),
                                        fieldWithPath("createdAt").type(JsonFieldType.STRING).description("리뷰 생성 시간"),
                                        fieldWithPath("modifiedAt").type(JsonFieldType.STRING).description("리뷰 수정 시간")
                                )
                        )
                ));
    }

    @Test
    void getReviewTest() throws Exception {
        // given
        Long reviewId = 1L;

        ReviewDto.Response response = new ReviewDto.Response(1L, "이미지", "텍스트",
                LocalDateTime.now(), LocalDateTime.now());

        given(compositeService.getReview(Mockito.anyLong())).willReturn(new Review());
        given(mapper.reviewToReviewResponseDto(Mockito.any(Review.class))).willReturn(response);

        // when
        ResultActions actions =
                mockMvc.perform(
                        RestDocumentationRequestBuilders
                                .get("/reviews/{review-id}", reviewId)
                                .accept(MediaType.APPLICATION_JSON)
                );

        // then
        actions
                .andExpect(status().isOk())
                .andDo(document(
                        "get-review",
                        getRequestPreProcessor(),
                        getResponsePreProcessor(),
                        pathParameters(
                                parameterWithName("review-id").description("리뷰 식별자 ID")
                        ),
                        responseFields(
                                List.of(
                                        fieldWithPath("id").type(JsonFieldType.NUMBER).description("리뷰 식별자"),
                                        fieldWithPath("reviewImage").type(JsonFieldType.STRING).description("리뷰 이미지"),
                                        fieldWithPath("reviewText").type(JsonFieldType.STRING).description("리뷰 텍스트"),
                                        fieldWithPath("createdAt").type(JsonFieldType.STRING).description("리뷰 생성 시간"),
                                        fieldWithPath("modifiedAt").type(JsonFieldType.STRING).description("리뷰 수정 시간")
                                )
                        )
                ));
    }

    @Test
    void getReviewsTest() throws Exception {
        // given
        String page = "1";
        String size = "10";
        MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
        queryParams.add("page", page);
        queryParams.add("size", size);

        Review review1 = new Review();
        Review review2 = new Review();

        Page<Review> pageReviews =
                new PageImpl<>(List.of(review1, review2),
                        PageRequest.of(0, 10, Sort.by("id").descending()), 2);

        List<ReviewDto.listResponse> responses = List.of(
                new ReviewDto.listResponse(1L, "이미지 1", "텍스트 1", LocalDateTime.now()),
                new ReviewDto.listResponse(2L, "이미지 2", "텍스트 2", LocalDateTime.now())
        );

        given(compositeService.getReviews(Mockito.anyInt(), Mockito.anyInt())).willReturn(pageReviews);
        given(mapper.reviewsToReviewResponseDto(Mockito.anyList())).willReturn(responses);

        // when
        ResultActions actions =
                mockMvc.perform(
                        get("/reviews")
                                .params(queryParams)
                                .accept(MediaType.APPLICATION_JSON)
                );

        // then
        MvcResult result = actions
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").isArray())
                .andDo(
                        document(
                                "get-reviews",
                                getRequestPreProcessor(),
                                getResponsePreProcessor(),
                                requestParameters(
                                        List.of(
                                                parameterWithName("page").description("Page 번호"),
                                                parameterWithName("size").description("Page 사이즈")
                                        )
                                ),
                                responseFields(
                                        List.of(
                                                fieldWithPath("data").type(JsonFieldType.ARRAY).description("결과 데이터").optional(),
                                                fieldWithPath("data[].id").type(JsonFieldType.NUMBER).description("리뷰 식별자"),
                                                fieldWithPath("data[].reviewImage").type(JsonFieldType.STRING).description("리뷰 이미지"),
                                                fieldWithPath("data[].reviewText").type(JsonFieldType.STRING).description("리뷰 텍스트"),
                                                fieldWithPath("data[].createdAt").type(JsonFieldType.STRING).description("리뷰 생성 시간"),
                                                fieldWithPath("pageInfo").type(JsonFieldType.OBJECT).description("페이지 정보"),
                                                fieldWithPath("pageInfo.page").type(JsonFieldType.NUMBER).description("페이지 번호"),
                                                fieldWithPath("pageInfo.size").type(JsonFieldType.NUMBER).description("페이지 사이즈"),
                                                fieldWithPath("pageInfo.totalElements").type(JsonFieldType.NUMBER).description("전체 건 수"),
                                                fieldWithPath("pageInfo.totalPages").type(JsonFieldType.NUMBER).description("전체 페이지 수")
                                        )
                                )
                        )
                ).andReturn();

        List list = JsonPath.parse(result.getResponse().getContentAsString()).read("$.data");

        assertThat(list.size(), is(2));
    }

    @Test
    void deleteReviewTest() throws Exception {
        // given
        Long reviewId = 1L;

        Principal principal = () -> "test@test.com";

        doNothing().when(compositeService).deleteReview(Mockito.anyLong(), Mockito.anyString());

        // when
        ResultActions actions =
                mockMvc.perform(
                        RestDocumentationRequestBuilders
                                .delete("/reviews/{review-id}", reviewId)
                                .with(user("test@test.com"))
                );

        // then
        actions
                .andExpect(status().isNoContent())
                .andDo(
                        document(
                                "delete-review",
                                getRequestPreProcessor(),
                                getResponsePreProcessor(),
                                pathParameters(
                                        Arrays.asList(parameterWithName("review-id").description("리뷰 식별자 ID"))
                                )
                        )
                );
    }
}

index.adoc


= UDog
:sectnums:
:toc: left
:toclevels: 4
:toc-title: Table of Contents
:source-highlighter: prettify

 유독 🐶

v1.0.0, 2023.03.17



***
== ReviewController
=== 리뷰 등록
.curl-request
include::{snippets}/post-review/curl-request.adoc[]

.http-request
include::{snippets}/post-review/http-request.adoc[]

.request-fields
include::{snippets}/post-review/request-fields.adoc[]

.http-response
include::{snippets}/post-review/http-response.adoc[]

.response-headers
include::{snippets}/post-review/response-headers.adoc[]

.response-fields
include::{snippets}/patch-review/response-fields.adoc[]

=== 질문 수정
.curl-request
include::{snippets}/patch-review/curl-request.adoc[]

.http-request
include::{snippets}/patch-review/http-request.adoc[]

.path-parameters
include::{snippets}/patch-review/path-parameters.adoc[]

.request-fields
include::{snippets}/patch-review/request-fields.adoc[]

.http-response
include::{snippets}/patch-review/http-response.adoc[]

.response-fields
include::{snippets}/patch-review/response-fields.adoc[]

=== 특정 리뷰 조회
.curl-request
include::{snippets}/get-review/curl-request.adoc[]

.http-request
include::{snippets}/get-review/http-request.adoc[]

.path-parameters
include::{snippets}/get-review/path-parameters.adoc[]

.http-response
include::{snippets}/get-review/http-response.adoc[]

.response-fields
include::{snippets}/get-review/response-fields.adoc[]


=== 전체 리뷰 조회
.curl-request
include::{snippets}/get-reviews/curl-request.adoc[]

.http-request
include::{snippets}/get-reviews/http-request.adoc[]

.request-parameters
include::{snippets}/get-reviews/request-parameters.adoc[]

.http-response
include::{snippets}/get-reviews/http-response.adoc[]

.response-fields
include::{snippets}/get-reviews/response-fields.adoc[]


=== 리뷰 삭제
.curl-request
include::{snippets}/delete-review/curl-request.adoc[]

.http-request
include::{snippets}/delete-review/http-request.adoc[]

.path-parameters
include::{snippets}/delete-review/path-parameters.adoc[]

.http-response
include::{snippets}/delete-review/http-response.adoc[]
***

API 명세서 (ver.1.0.0)













profile
백엔드 개발자 김창하입니다 🙇‍♂️

0개의 댓글