- MemberErrorCode
<hide/>
@Getter
@AllArgsConstructor
public enum MemberErrorCode {
/**
* 회원가입 - 이미 존재하는 이메일, 휴대폰, 닉네임 오류
*/
EXIST_EMAIL("이미 가입된 이메일입니다."),
EXIST_PHONE("동일한 휴대폰 번호가 존재합니다. 다른 번호를 입력하세요."),
EXIST_NICKNAME("동일한 닉네임이 존재합니다. 다른 닉네임을 입력하세요"),
/**
* 회원가입 - 비밀번호 오류
*/
PASSWORD_LENGTH_MORE_THAN_8("비밀번호는 8자리 이상이어야합니다.(영문자, 숫자, 특수문자를 각각 1글자 이상 포함)"),
PASSWORD_NOT_CONTAIN_DIGIT("비밀번호는 숫자를 한 글자 이상 포함해야합니다."),
PASSWORD_NOT_CONTAIN_SPECIAL("비밀번호는 특수 문자를 한 글자 이상 포함해야합니다."),
PASSWORD_NOT_CONTAIN_CHARACTER("비밀번호는 영문자를 한 글자 이상 포함해야합니다."),
PASSWORD_NOT_CONTAIN_DIGIT_AND_SPECIAL("비밀번호는 숫자, 특수 문자를 각각 한 글자 이상 포함해야합니다."),
PASSWORD_NOT_CONTAIN_DIGIT_AND_CHARACTER("비밀번호는 숫자, 영문자 문자를 각각 한 글자 이상 포함해야합니다."),
PASSWORD_NOT_CONTAIN_CHARACTER_AND_SPECIAL("비밀번호는 영문자, 특수 문자를 각각 한 글자 이상 포함해야합니다."),
/**
* 회원 조회 오류
*/
NOT_EXIST_MEMBER("회원 정보가 존재하지 않습니다."),
NOT_EXIST_EMAIL("이메일이 존재하지 않습니다."),
NOT_EXIST_PHONE("연락처(번호)가 존재하지 않습니다."),
/**
* 회원 정보 수정 오류
*/
CONTAINS_BLANK("회원 정보는 공백을 포함할 수 없습니다."),
/**
* 비밀번호 오류
*/
NOT_MATCH_PASSWORD("회원 비밀번호를 잘못 입력했습니다.");
private final String description;
}
- MemberException
<hide/>
@Getter
@Setter
@AllArgsConstructor
public class MemberException extends RuntimeException {
public MemberErrorCode errorCode;
}
- MemberErrorResponse
<hide/>
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class MemberErrorResponse {
private MemberErrorCode errorCode;
private String message;
}
- ExceptionHandler
<hide/>
@RestControllerAdvice
public class CustomExceptionHandler {
@ExceptionHandler(IncomeException.class)
protected ResponseEntity<IncomeErrorResponse> incomeHandlerCustomException(IncomeException e) {
IncomeErrorResponse errorResponse = IncomeErrorResponse.builder()
.errorCode(e.getErrorCode())
.message(e.getErrorCode().getDescription())
.build();
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(ExpenseException.class)
protected ResponseEntity<ExpenseErrorResponse> incomeHandlerCustomException(
ExpenseException e) {
ExpenseErrorResponse errorResponse = ExpenseErrorResponse.builder()
.errorCode(e.getErrorCode())
.message(e.getErrorCode().getDescription())
.build();
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(MemberException.class)
protected ResponseEntity<MemberErrorResponse> MemberHandlerCustomException(
MemberException e) {
MemberErrorResponse errorResponse = MemberErrorResponse.builder()
.errorCode(e.getErrorCode())
.message(e.getErrorCode().getDescription())
.build();
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
}
- ImplTest
- 테스트 코드에 예외처리 적용하기
<hide/>
@Test
@DisplayName("회원 정보 수정(공백 포함) - 실패 (1)")
void updateInfo_fail_blank() {
// given
Member member = Member.builder().email("egg@naver.com").address("서울특별시")
.phone("010-2222-0000").birth(LocalDate.from(LocalDate.of(2000, 9, 30))).gender("남")
.nickName("강동원").password("abc!@#12").build();
given(memberRepository.findByEmail(anyString())).willReturn(Optional.of(member));
UpdateMemberInput input = UpdateMemberInput.builder().address("강원도").nickName("치킨")
.phone("010-11 11-2313").birth(LocalDate.now()).memberPhotoUrl("c:").gender("여")
.address("강원도").build();
// when
MemberException exception = assertThrows(MemberException.class,
() -> memberService.updateInfo(member.getEmail(), input));
// then
assertEquals(MemberErrorCode.CONTAINS_BLANK.getDescription(),
exception.getErrorCode().getDescription());
}
@Test
@DisplayName("회원 정보 수정(같은 폰번호가 존재하는 경우) - 실패 (2)")
void updateInfo_fail_sameNickName() {
// given
Member memberA = Member.builder().email("egg@naver.com").address("서울특별시")
.phone("010-2222-0000").birth(LocalDate.from(LocalDate.of(2000, 9, 30))).gender("남")
.nickName("강동원").password("abc!@#12").build();
given(memberRepository.findByEmail(any())).willReturn(Optional.of(memberA));
Member memberB = Member.builder().email("ogh@naver.com").address("인천광역시")
.phone("010-9999-0000").birth(LocalDate.from(LocalDate.of(1998, 9, 30))).gender("남")
.nickName("소지섭").password("abc!@#12").build();
given(memberRepository.findByPhone(any())).willReturn(Optional.of(memberB));
UpdateMemberInput input = UpdateMemberInput.builder().address("강원도").nickName("강동원")
.phone("010-9999-0000").birth(LocalDate.now()).memberPhotoUrl("c:").gender("여")
.address("강원도").build();
// when
MemberException exception = assertThrows(MemberException.class,
() -> memberService.updateInfo(memberB.getEmail(), input));
// then
assertEquals(MemberErrorCode.EXIST_PHONE.getDescription(),
exception.getErrorCode().getDescription());
}
@Test
@DisplayName("회원 정보 수정(같은 닉네임이 존재하는 경우) - 실패(3)")
void updateInfo_fail_samePhoneNumber() {
// given
Member memberA = Member.builder().email("egg@naver.com").address("서울특별시")
.phone("010-2222-0000").birth(LocalDate.from(LocalDate.of(2000, 9, 30))).gender("남")
.nickName("강동원").password("abc!@#12").build();
given(memberRepository.findByEmail(any())).willReturn(Optional.of(memberA));
Member memberB = Member.builder().email("ogh@naver.com").address("인천광역시")
.phone("010-9999-0000").birth(LocalDate.from(LocalDate.of(1998, 9, 30))).gender("남")
.nickName("소지섭").password("abc!@#12").build();
given(memberRepository.findByNickName(any())).willReturn(Optional.of(memberB));
UpdateMemberInput input = UpdateMemberInput.builder().address("강원도").nickName("소지섭")
.phone("010-2222-0000").birth(LocalDate.now()).memberPhotoUrl("c:").gender("여")
.address("강원도").build();
// when
MemberException exception = assertThrows(MemberException.class,
() -> memberService.updateInfo(memberA.getEmail(), input));
// then
assertEquals(MemberErrorCode.EXIST_NICKNAME.getDescription(),
exception.getErrorCode().getDescription());
}
@Test
@DisplayName("비밀번호 변경 - 성공")
void updatePassword() {
// given
Member member = Member.builder().email("egg@naver.com").address("서울특별시")
.phone("010-2222-0000").birth(LocalDate.from(LocalDate.of(2000, 9, 30))).gender("남")
.nickName("강동원").build();
String encPassword = bCryptPasswordEncoder.encode("abc!@#12");
member.setPassword(encPassword);
given(memberRepository.findByEmail("egg@naver.com")).willReturn(Optional.of(member));
// when
String email = "egg@naver.com";
ChangePasswordInput input = ChangePasswordInput.builder().password("abc!@#12")
.newPassword("xyz@#123").build();
// then
memberService.changePassword(email, input);
}
@Test
@DisplayName("비밀번호 변경 - 실패")
void updatePassword_fail() {
// given
Member member = Member.builder().email("egg@naver.com").address("서울특별시")
.phone("010-2222-0000").birth(LocalDate.from(LocalDate.of(2000, 9, 30))).gender("남")
.nickName("강동원").build();
String encPassword = bCryptPasswordEncoder.encode("abc!@#12");
member.setPassword(encPassword);
given(memberRepository.findByEmail(anyString())).willReturn(Optional.of(member));
// when
String email = "egg@naver.com";
ChangePasswordInput input = ChangePasswordInput.builder().password("abc!@#12")
.newPassword("xyz").build();
// then
MemberException exception = assertThrows(MemberException.class,
() -> memberService.changePassword(email, input));
assertEquals(MemberErrorCode.PASSWORD_LENGTH_MORE_THAN_8.getDescription(),
exception.getErrorCode().getDescription());
}
- Ctrl test
- 컨트롤러에서 세션에 로그인된 정보를 가져오기 위해 Principal 객체를 이용할 경우, 테스트 코드에서는 이를 어떻게 적용할까?
- 아래와 같이 User와 TestingAuthenticationToken을 이용해서 테스트 코드에 적용가능하다.
User user = new User(email, pwd, AuthorityUtils.NO_AUTHORITIES);
TestingAuthenticationToken testingAuthenticationToken = new TestingAuthenticationToken(user, null);
<hide/>
@Test
@DisplayName("회원 비밀번호 변경 - 성공")
void updatePassword() throws Exception {
// given
ChangePasswordInput input = ChangePasswordInput.builder().password("abc123!@")
.newPassword("xyz098?!").build();
String inputToJson = mapper.writeValueAsString(input);
String email = "egg@naver.com";
String pwd = "123abc?!";
User user = new User(email, pwd, AuthorityUtils.NO_AUTHORITIES);
TestingAuthenticationToken testingAuthenticationToken = new TestingAuthenticationToken(user,
null);
//when
mockMvc.perform(patch("/api/member/password").contentType(MediaType.APPLICATION_JSON)
.principal(testingAuthenticationToken).content(inputToJson)).andExpect(status().isOk())
.andDo(print());
ArgumentCaptor<ChangePasswordInput> captor = ArgumentCaptor.forClass(
ChangePasswordInput.class);
// then
Mockito.verify(memberServiceImpl, times(1)).changePassword(anyString(), captor.capture());
assertEquals(captor.getValue().getNewPassword(), input.getNewPassword());
}
'Spring Projcect > [팀플] In & Out 가계부' 카테고리의 다른 글
회원 가입 API (0) | 2022.10.27 |
---|---|
회원 로그아웃 API (0) | 2022.10.26 |
회원 로그인 API (0) | 2022.10.25 |
회원 정보 조회 및 수정 API (0) | 2022.10.21 |
회원 아이디 & 비밀번호 찾기 (0) | 2022.10.19 |