- UpdateMemberInput
<hide/>
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UpdateMemberInput {
private String nickName;
private String phone;
private LocalDate birth;
private String address;
private String gender;
private String memberPhotoUrl;
}
- MemberDto
- Member 엔티티를 직접 건드리지 않고 member 객체를 이용할 수 있다.
- UpdateMemberInput과 거의 동일하다.
<hide/>
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MemberDto {
private String nickName;
private String phone;
private LocalDate birth;
private String address;
private String gender;
private String memberPhotoUrl;
public static MemberDto toDto(Member member) {
return MemberDto.builder()
.address(member.getAddress())
.birth(member.getBirth())
.gender(member.getGender())
.memberPhotoUrl(member.getMemberPhotoUrl())
.nickName(member.getNickName())
.phone(member.getPhone())
.build();
}
}
- Service
<hide/>
@Override
public void updateInfo(String email, UpdateMemberInput input) {
Member member = memberRepository.findByEmail(email).get();
if (input.getNickName().contains(" ") ||
input.getPhone().contains(" ") ||
input.getAddress().contains(" ") ||
input.getMemberPhotoUrl().contains(" ") ||
input.getGender().contains(" ")) {
throw new RuntimeException("회원 정보는 공백을 포함할 수 없습니다.");
}
String previousUsedPhone = member.getPhone();
String previousUsedNickname = member.getNickName();
if (previousUsedPhone.equals(input.getPhone())) {
throw new RuntimeException("기존 연락처와 동일합니다.");
}
if (previousUsedNickname.equals(input.getNickName())) {
throw new RuntimeException("기존 닉네임과 동일합니다.");
}
member.setNickName(input.getNickName());
member.setPhone(input.getPhone());
member.setBirth(input.getBirth()); // null()
member.setAddress(input.getAddress());
member.setGender(input.getGender());
member.setMemberPhotoUrl(input.getMemberPhotoUrl());
memberRepository.save(member);
}
- Controller
<hide/>
@PutMapping("/member/info")
public ResponseEntity<?> updateInfo(/*Principal principal*/
@RequestBody UpdateMemberInput input) {
// String email = principal.getName();
String email = "egg@naver.com";
memberService.updateInfo(email, input);
String message = "회원 정보를 변경했습니다.";
return new ResponseEntity<>(message, HttpStatus.OK);
}
- ImplTest
- captor클래스: 어떤 순간의 값을 저장하는 클래스 , 어떤 형태의 값을 캡터할 것인지 클래스명을 쓴다.
- 위처럼 설정하면 String 만 캡처 가능하다.
- verify() 맨 뒤에서 캡터를 사용해야한다.
- times(1): verify() 안에 있는 메서드를 한 번 실행한다.
- getInfo()를 한 번 실행하는지 확인하겠다.
- times()는 메서드가 실행 횟수를 체크한다.
- any() : memberServiceIm 이 실행되는지만 확인하려면 getInfo() 안에 any()를 넣을 수도 있다.
- given은 테스트메서드 내에서 반환형을 특별히 정해준다. 진짜 리포짓에서 찾아오는게 아니니까 리포짓에서 찾아온다 가정했을 때의 반환값인 member를 넣어준다.
- 아래 테스트 메서드에서 findByEmail() 안에는 any() 또는 "egg@naver.com"둘 중 하나만 들어가야 통과되며 다른 값이 들어가면 테스트를 통과하지 못한다.
<hide/>
@Test
@DisplayName("회원 정보 수정 - 성공")
void updateInfo() {
// 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)
);
// when
UpdateMemberInput input = UpdateMemberInput.builder().address("강원도")
.nickName("치킨")
.phone("010-1111-2313")
.birth(LocalDate.now())
.memberPhotoUrl("c:")
.gender("여")
.address("강원도")
.build();
// then
memberService.updateInfo("egg@naver.com", input);
}
@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
Exception exception = assertThrows(RuntimeException.class,
() -> memberService.updateInfo(member.getEmail(), input));
// then
assertEquals(exception.getMessage(), "회원 정보에는 공백을 포함할 수 없습니다.");
}
@Test
@DisplayName("회원 정보 수정(이전의 닉네임과 동일) - 실패 (2)")
void updateInfo_fail_sameNickName() {
// 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("egg@naver.com")).willReturn(
Optional.of(member)
);
UpdateMemberInput input = UpdateMemberInput.builder().address("강원도")
.nickName("강동원")
.phone("010-1111-2313")
.birth(LocalDate.now())
.memberPhotoUrl("c:")
.gender("여")
.address("강원도")
.build();
// when
Exception exception = assertThrows(RuntimeException.class,
() -> memberService.updateInfo(member.getEmail(), input));
// then
assertEquals(exception.getMessage(), "기존 닉네임과 동일합니다.");
}
@Test
@DisplayName("회원 정보 수정(이전의 폰번호와 동일) - 실패(3)")
void updateInfo_fail_samePhoneNumber() {
// 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(any())).willReturn(
Optional.of(member)
);
// when
UpdateMemberInput input = UpdateMemberInput.builder().address("강원도")
.nickName("동원참치")
.phone("010-2222-0000")
.birth(LocalDate.now())
.memberPhotoUrl("c:")
.gender("여")
.address("강원도")
.build();
RuntimeException exception = assertThrows(RuntimeException.class,
() -> memberService.updateInfo(member.getEmail(), input));
// then
assertEquals(exception.getMessage(), "기존 연락처와 동일합니다.");
}
- ctrl 테스트
- 컨트롤러 테스트는 일단 성공 케이스만 작성한다.
<hide/>
@Test
@DisplayName("회원 정보 수정 - 성공")
void updateInfo() throws Exception {
// given
UpdateMemberInput input = UpdateMemberInput.builder().address("강원도")
.nickName("치킨")
.phone("010-1111-2313")
.birth(LocalDate.now())
.memberPhotoUrl("c:")
.gender("여")
.address("강원도")
.build();
String inputToJson = mapper.writeValueAsString(input);
// when
mockMvc.perform(
put("/api/member/info")
.contentType(MediaType.APPLICATION_JSON)
.content(inputToJson))
.andExpect(status().isOk())
.andDo(print());
ArgumentCaptor<UpdateMemberInput> captor = ArgumentCaptor.forClass(UpdateMemberInput.class);
// then
Mockito.verify(memberServiceImpl, times(1)).updateInfo(anyString(), captor.capture());
assertEquals(captor.getValue().getPhone(), input.getPhone()); //
}
- 오류: beanCreationException 빈 생성 오류
<hide/>
rg.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSourceScriptDatabaseInitializer' defined in class path resource [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]: Invocation of init method failed; nested exception is org.springframework.jdbc.datasource.init.UncategorizedScriptException: Failed to execute database script from resource [URL [file:/C:/Users/Ran/Desktop/R/zerobase/team_project/rezero/server/build/resources/main/data.sql]]; nested exception is java.lang.IllegalArgumentException: 'script' must not be null or empty
- 원인: data.sql 이라는 DB 관련 파일을 추가했는데 yml 파일이나 builde.gradle 파일에 관련추가하지 않았다.
- 해결: data.sql 사용하고 있는 파일이 아니라서 삭제했다.
'Spring Projcect > [팀플] In & Out 가계부' 카테고리의 다른 글
회원 가입 API (0) | 2022.10.27 |
---|---|
회원 로그아웃 API (0) | 2022.10.26 |
회원 예외 처리 MemberErrorCode, ExceptionHandler (0) | 2022.10.25 |
회원 로그인 API (0) | 2022.10.25 |
회원 아이디 & 비밀번호 찾기 (0) | 2022.10.19 |