본문 바로가기
Project/깃터디 (gitudy)

[깃터디/Auth] 닉네임 중복검사 api 구현

by J-rain 2024. 5. 10.

💡 .java를 클릭시 관련 커밋으로 이동💡

 

 

 회고 

Entitiy 조회보단 -> Dto 조회가 성능상 유리

QueryDsl에서 exist 메서드 쓰는것보다 JPA의 exists() 메서드를 사용했다. 그이유가 JPA exists()메서드는 선택된 항이 하나라도 존재하면 즉시 처리를 멈추고 결과를 반환하는데 QueryDsl exist 메서드는 실제로 count() > 0 으로 실행한다.

 

// 참고한 자료

https://www.youtube.com/watch?v=zMAX7g6rO_Y

https://yainii.tistory.com/36

    @Test
    void 닉네임_중복체크_테스트_중복인경우() {
        // given
        userRepository.save(generateAuthUser());

        UserNameRequest request = UserFixture.generateUserNameRequest("이름");

        // then
        assertThrows(UserException.class, () -> {
            authService.nickNameDuplicationCheck(request);
        }, ExceptionMessage.USER_NAME_DUPLICATION.getText());
      }

테스트를 작성하면서 신기한 점을 발견했다. // then 부분에서 ExceptionMessage.USER_NAME_DUPLICATION.getText() 이부분을 다른 예외 메시지로 바꿔도 통과가 되버리는 상황이 발생했는데..

찾아보니 JUnit에서 assertThrows() 메서드를 사용할때는 두가지 방법으로 예외를 검증한다.

  • 예외 타입만 검증하는 경우 위의 코드처럼 예외가 발생하는 지만 검증한다. 즉, 예외 메시지는 따로 검증하지 않는다.
  • 예외 타입과 메시지를 검증하는 경우는 아래와 같다.
UserException em = assertThrows(UserException.class, () -> {
    authService.nickNameDuplicationCheck(request);
});
assertEquals(ExceptionMessage.USER_NAME_DUPLICATION.getText(), em.getMessage());

이코드로 두단계를 걸쳐 예외가 발생하는지 검증하고 그다음 예외 메시지를 따로 검증해야 한다!!!

 


AuthController.java

    @ApiResponse(responseCode = "200", description = "닉네임 중복 검사 성공")
    @PostMapping("/check-nickname")
    public JsonResult<?> nickNameDuplicationCheck(@Valid @RequestBody UserNameRequest request) {

        authService.nickNameDuplicationCheck(request);

        return JsonResult.successOf("Nickname Duplication Check Success.");
    }

닉네임 중복검사를 위한 컨트롤러 구현이다.

 

 

UserNameRequest.java

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserNameRequest {

    @NotBlank(message = "이름은 공백일 수 없습니다.")
    @Size(max = 6, message = "이름 6자 이내")
    private String name;  // 이름

}

User가 작성하는 이름 DTO 이다.

 

 

AuthService.java

    // 닉네임 중복체크 메서드
    public void nickNameDuplicationCheck(UserNameRequest request) {

        boolean exists = userRepository.existsByName(request.getName());

        if (exists) {
            log.warn(">>>> {} : {} <<<<", request.getName(), ExceptionMessage.USER_NAME_DUPLICATION);
            throw new UserException(ExceptionMessage.USER_NAME_DUPLICATION);
        }

    }

existsByName() 메서드를 통해 데이터베이스에 같은 이름이 있는지 검사를 한다. 만약 있을 경우 true로 반환되어 예외가 터진다.

 

UserRepository.java

 // 같은 Name(닉네임)이 있는지 판별한다.
    boolean existsByName(String name);

 

 


테스트

 

AuthControllerTest.java

    @Test
    void 닉네임_중복체크_테스트() throws Exception {
        // given
        UserNameRequest request = UserFixture.generateUserNameRequest("이정우");

        doNothing().when(authService).nickNameDuplicationCheck(any(UserNameRequest.class));

        // when & then
        mockMvc.perform(post("/auth/check-nickname")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(request)))

                // then
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.res_code").value(200))
                .andExpect(jsonPath("$.res_msg").value("OK"))
                .andExpect(jsonPath("$.res_obj").value("Nickname Duplication Check Success."))
                .andDo(print());
    }

    @Test
    void 닉네임_중복체크_유효성_검증_실패_테스트1() throws Exception {
        // given
        String inValidName = "   ";
        String expectedError = "name: 이름은 공백일 수 없습니다.";

        UserNameRequest request = UserFixture.generateUserNameRequest(inValidName);

        doNothing().when(authService).nickNameDuplicationCheck(any(UserNameRequest.class));

        // when
        mockMvc.perform(post("/auth/check-nickname")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(request)))

                // then
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.res_code").value(400))
                .andExpect(jsonPath("$.res_msg").value(expectedError))
                .andDo(print());
    }

    @Test
    void 닉네임_중복체크_유효성_검증_실패_테스트2() throws Exception {
        // given
        String inValidName = "이름은6자이내";
        String expectedError = "name: 이름 6자 이내";

        UserNameRequest request = UserFixture.generateUserNameRequest(inValidName);

        doNothing().when(authService).nickNameDuplicationCheck(any(UserNameRequest.class));

        // when
        mockMvc.perform(post("/auth/check-nickname")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(request)))

                // then
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.res_code").value(400))
                .andExpect(jsonPath("$.res_msg").value(expectedError))
                .andDo(print());
    }

동작이 잘 되는지 컨트롤러 테스트인데 추가적으로 이름을 보내는 DTO에서 유효성 검증에 실패하는 경우를 나누어서 테스트했다.

 

 

AuthServiceTest.java

    @Test
    void 닉네임_중복체크_테스트_중복인경우() {
        // given
        userRepository.save(generateAuthUser());

        UserNameRequest request = UserFixture.generateUserNameRequest("이름");

        // then
        UserException em = assertThrows(UserException.class, () -> {
            authService.nickNameDuplicationCheck(request);
        });

        assertEquals(ExceptionMessage.USER_NAME_DUPLICATION.getText(), em.getMessage());
        }
        
        
    @Test
    void 닉네임_중복체크_테스트_중복이아닌경우() {
        // given
        userRepository.save(generateAuthUser());

        UserNameRequest request = UserFixture.generateUserNameRequest("이정우");

        // when
        authService.nickNameDuplicationCheck(request);
        
        // then
        assertDoesNotThrow(() -> authService.nickNameDuplicationCheck(request));
    }

중복인 경우와 중복이 아닌 경우를 확인하는 Service 테스트이다.

 

 

UserFixture.java

public static UserNameRequest generateUserNameRequest(String name) {
        return UserNameRequest.builder()
                .name(name)
                .build();
    }

 

DTO를 전달하기 위한 테스트용 Fixture 작성

댓글