SEB_BE_43 / 23.03.09 회고

rse·2023년 3월 9일
0

코드스테이츠_BE_43

목록 보기
50/65

오늘

  • API 실습

실습

결과물

코드

MemberController 의 get 부분

@Test
    public void getMemberTest() throws Exception {
        // TODO 여기에 MemberController의 getMember() 핸들러 메서드 API 스펙 정보를 포함하는 테스트 케이스를 작성 하세요.
        // given 테스트 준비
        long memberId = 1L;
        Member member = new Member("hgd@gmail.com", "홍길동", "010-1010-1111");
        member.setStamp(new Stamp());
        member.setMemberId(1L);

        MemberDto.Response response = new MemberDto.Response(1L, "hgd@gmail.com", "홍길동",
                "010-1010-1111", Member.MemberStatus.MEMBER_ACTIVE, new Stamp());

        given(memberService.findMember(Mockito.anyLong()))
                .willReturn(new Member());
        given(mapper.memberToMemberResponse(Mockito.any(Member.class)))
                .willReturn(response);

        // when 테스트 대상
        ResultActions actions =
                mockMvc.perform(get("/v11/members/{memberId}", memberId)
                        .accept(MediaType.APPLICATION_JSON)
                );
        // then 테스트 결과
        actions
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.memberId").value(member.getMemberId()))
                .andExpect(jsonPath("$.data.email").value(member.getEmail()))
                .andExpect(jsonPath("$.data.name").value(member.getName()))
                .andExpect(jsonPath("$.data.phone").value(member.getPhone()))
                .andDo(document("get-member",
                        getResponsePreProcessor(),
                        pathParameters(
                                parameterWithName("memberId").description("회원 식별자")
                        ),
                        responseFields(
                                List.of(
                                        fieldWithPath("data").type(JsonFieldType.OBJECT).description("결과 데이터"),
                                        fieldWithPath("data.memberId").type(JsonFieldType.NUMBER).description("회원 식별자"),
                                        fieldWithPath("data.email").type(JsonFieldType.STRING).description("이메일"),
                                        fieldWithPath("data.name").type(JsonFieldType.STRING).description("이름"),
                                        fieldWithPath("data.phone").type(JsonFieldType.STRING).description("핸드폰"),
                                        fieldWithPath("data.memberStatus").type(JsonFieldType.STRING).
                                                description("회원 상태: 활동중 / 휴면상태 / 탈퇴 상태"),
                                        fieldWithPath("data.stamp").type(JsonFieldType.NUMBER).description("스탬프 갯수")
                                        )
                        )));
    }

given(memberService) 부분에서 willReturn(new Member()); 가 되는 이유는 그 다음 로직에서 member가 쓰이지 않기 때문이다. 정확히 말하면 그 다음 로직인 mapper.memberTo~ 부분도 Mock으로 지정해줘서 가짜 객체이기 때문에 안쓰인다고 생각하면 되겠다.

하지만 willReturn 값으로 null을 넣어서는 안된다. 에러뜬다. 안에 내용이 없는 빈 객체라도 new로 생성해서 넣어줘야 함.


MemberController의 findAll부분

@Test
public void getMembersTest() throws Exception {
     // TODO 여기에 MemberController의 getMembers() 핸들러 메서드 API 스펙 정보를 포함하는 테스트 케이스를 작성 하세요.
     // given
     Member member1 = new Member("hgd@gmail.com", "홍길동", "010-1111-1111");
     member1.setStamp(new Stamp());
     member1.setMemberStatus(Member.MemberStatus.MEMBER_ACTIVE);
     Member member2 = new Member("spring@gmail.com", "스프링", "010-2222-2222");
     member2.setStamp(new Stamp());
     member2.setMemberStatus(Member.MemberStatus.MEMBER_ACTIVE);

     Page<Member> pageMembers = new PageImpl<>(List.of(member1, member2), PageRequest.of(0, 10, Sort.by("memberId").descending()), 2);

     List<MemberDto.Response> responses = List.of(
           new MemberDto.Response(1L, "hgd@gmail.com", "홍길동", "010-1111-1111",
                Member.MemberStatus.MEMBER_ACTIVE, new Stamp()),
           new MemberDto.Response(2L, "spring@gmail.com", "스프링", "010-2222-2222",
                Member.MemberStatus.MEMBER_ACTIVE, new Stamp())
        );

     given(memberService.findMembers(Mockito.anyInt(), Mockito.anyInt()))
                .willReturn(pageMembers);
     given(mapper.membersToMemberResponses(Mockito.anyList()))
                .willReturn(responses);

     // when
     ResultActions actions =
            mockMvc.perform(
                    get("/v11/members")
                            .param("page", "1")
                            .param("size", "10")
                            .accept(MediaType.APPLICATION_JSON)
        );

     // then
     actions
             .andExpect(status().isOk())
             .andExpect(jsonPath("$.data").isArray())
             .andDo(document("get-members",
                     getResponsePreProcessor(),  // get은 요청 body가 없고, 응답 body만 있으니 ResponsePreProcessor를 적어줌.
                     requestParameters(  // 요청할 때 URL에 작성하는 파라미터 값.
                             parameterWithName("page").description("페이지"),
                             parameterWithName("size").description("사이즈")
                        ),
                     responseFields(  // 응답 body(필드)
                             List.of( // 객체 이름과 같아야함.
                                     fieldWithPath("data").type(JsonFieldType.ARRAY).description("결과 데이터").optional(), // 데이터가 한 건도 없을 경우 null 방지
                                     fieldWithPath("data[].memberId").type(JsonFieldType.NUMBER).description("회원 식별자"),
                                     fieldWithPath("data[].email").type(JsonFieldType.STRING).description("이메일"),
                                     fieldWithPath("data[].name").type(JsonFieldType.STRING).description("이름"),
                                     fieldWithPath("data[].phone").type(JsonFieldType.STRING).description("핸드폰"),
                                     fieldWithPath("data[].memberStatus").type(JsonFieldType.STRING).description("회원 상태: 활동중 / 휴면 상태 / 탈퇴 상태"),
                                     fieldWithPath("data[].stamp").type(JsonFieldType.NUMBER).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("총 페이지 수")
                                )
                        )));
    }
@Getter
public class MultiResponseDto<T> {
    private List<T> data;
    private PageInfo pageInfo;

    public MultiResponseDto(List<T> data, Page page) {
        this.data = data;
        this.pageInfo = new PageInfo(page.getNumber() + 1,
                page.getSize(), page.getTotalElements(), page.getTotalPages());
    }
}
@AllArgsConstructor
    @Getter
    public static class Response {
        private long memberId;
        private String email;
        private String name;
        private String phone;
        private Member.MemberStatus memberStatus;
        private Stamp stamp;

        public String getMemberStatus() {
            return memberStatus.getStatus();
        }
        public int getStamp() {
            return stamp.getStampCount();
        }
    }

findAll같은 경우 Controller 에 보면 Page라는 인터페이스에서 service를 사용하고,
List에서 pageMembers에서 값을 가져온다.

그렇기에 Mockito의 willReturn 값으로 가짜 값을 넣어줘서는 안된다.

값을 리턴해줄 때 생각해야하는 부분
그 다음 로직에서 그 값을 사용하는가?

 ResultActions actions =
                mockMvc.perform(
                        get("/v11/members")
                                .param("page", "1")
                                .param("size", "10")
                                .accept(MediaType.APPLICATION_JSON)
        );

이 부분에서 .param은 controller에서

RequestParam으로 값을 받고 있기 때문에 넣어준 것.

Postman에서 Response 데이터를 보면 "data" : [ 되어있고, 그 안에 { 해서 데이터가 들어있으며, "pageInfo"는 또 따로 되어있는 것을 알 수 있다.

Json 에서는 여러건의 데이터들이 있을 경우 대괄호로 표현한다.

그래서

responseFields(
        List.of(
                fieldWithPath("data").type(JsonFieldType.ARRAY).description("결과 데이터").optional(), 
                fieldWithPath("data[].memberId").type(JsonFieldType.NUMBER).description("회원 식별자"),
                fieldWithPath("data[].email").type(JsonFieldType.STRING).description("이메일"),
                fieldWithPath("data[].name").type(JsonFieldType.STRING).description("이름"),
                fieldWithPath("data[].phone").type(JsonFieldType.STRING).description("핸드폰"),
                fieldWithPath("data[].memberStatus").type(JsonFieldType.STRING).description("회원 상태: 활동중 / 휴면 상태 / 탈퇴 상태"),
                fieldWithPath("data[].stamp").type(JsonFieldType.NUMBER).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("총 페이지 수")
                                )
                        )));
    }

이 부분에서 data[].memberId 를 해준것이다. 대괄호는 [] 배열이기에.

MemberController에서 delete

@Test
public void deleteMemberTest() throws Exception {
	// given
	long memberId = 1L;
	Member member = new Member("hgd@naver.com", "홍길동", "010-1111-1111");
    member.setMemberId(1L);
    doNothing().when(memberService).deleteMember(memberId);
    
    //when
    ResultActions actions =
			mockMvc.perform(delete("/v11/members/{member-id}", memberId)  
            // API 문서를 작성 할 때는 perform() 란에 string을 사용하자. 오류가 발생 할 수 있다.
			); 
    
    //then
    actions
          .andExpect(status().isNoContent())
          .andDo(document("delete-member",
                  pathParameters(
                          parameterWithName("member-id").description("회원 식별자")
                  )
                  ));
                  

추가 기능

여러가지 기능들이 있지만 한가지의 기능만 추가해보겠다.

필수값 여부

Patch에서 .optional() 을 사용했는데 이런 경우 API문서에 해당 필드가 필수인지 아닌지를 적어주면 좋을 것이다.

Request-fields의 스니펫을 사용하려면
src/test/resources/org/springframework/restdocs/templates.request-fields.snippet
파일을 추가하자.

스니펫은 mustache 문법을 사용한다.

내용은 아래 사진 참고. |필드명 적고 | 타입 적고 | 필수값 여부(true / false) | 설명

이렇게 작성해준 뒤 API 문서를 보면

필수값 여부가 나타나는 것을 볼 수 있다.

profile
기록을 합시다

0개의 댓글