이전 이야기
https://record47584.tistory.com/90
Spring 프로젝트 되살리기 5 - 리펙토링 3 (@EntityGraph 적용)
이전 이야기https://record47584.tistory.com/89 Spring 프로젝트 되살리기 4 - 코드 리펙토링 2 (@Valid 적용)이전 이야기https://record47584.tistory.com/88 Spring 프로젝트 되살리기 3 - 코드 리펙토링 1 (중첩 if문 제거
record47584.tistory.com
시나리오 6
이제 코드에 큰 문제는 없어보인다. 유닛 테스트를 진행하려는데 실패하는 테스트들이 존재한다.
프로젝트 코드가 문제인가? 테스트 코드가 문제인가? 확인하고 수정해보자.
트러블 슈팅 1
@Test
public void manager_목록_조회_시_Todo가_없다면_NPE_에러를_던진다() {
// given
long todoId = 1L;
given(todoRepository.findById(todoId)).willReturn(Optional.empty());
// when & then
ServerException exception = assertThrows(ServerException.class, () -> managerService.getManagers(todoId));
assertEquals("Manager not found", exception.getMessage());
}
검색한 id의 todo가 없을 경우, NullPointException을 리턴하는지 체크하는 테스트 코드이다.

실행하면 위와 같이 테스트에 실패한다.
exception 변수에 ServerException이 아니라 InvalidRequestException이 들어왔단다.
@Transactional(readOnly = true)
public List<ManagerResponse> getManagers(long todoId) {
Todo todo = todoRepository.findById(todoId)
.orElseThrow(() -> new InvalidRequestException("Todo not found"));
List<Manager> managerList = managerRepository.findByTodoIdWithUser(todo.getId());
List<ManagerResponse> dtoList = new ArrayList<>();
for (Manager manager : managerList) {
User user = manager.getUser();
dtoList.add(new ManagerResponse(
manager.getId(),
new UserResponse(user.getId(), user.getEmail())
));
}
return dtoList;
}
프로젝트 코드를 찾아보니까 todo 검색에 실패하면 InvalidRequestException을 리턴한다.
결국 둘이 불일치 하므로 문맥이 더 자연스러운 쪽으로 한 쪽을 수정해야 한다.
파라미터로 받은 todo id로 todo를 찾지 못해서 발생한 이슈이기 때문에 InvalidRequestException가 발생하는게 자연스러워 보인다.
테스트 코드에서 문맥에 어긋한 부분만 수정하면 된다.
@Test
public void manager_목록_조회_시_Todo가_없다면_IRE_에러를_던진다() {
// given
long todoId = 1L;
given(todoRepository.findById(todoId)).willReturn(Optional.empty());
// when & then
InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> managerService.getManagers(todoId));
assertEquals("Todo not found", exception.getMessage());
}
- 메서드 명부터 잘못됐으니 수정 (NPE_에러를_던진다 -> IRE_에러를_던진다)
- managerService.getManagers로 부터 InvalidRequestException를 리턴 받으니까 변수 타입 변경
- 에러 문구도 Manager가 아니라 Todo를 못 찾았으니까, "Todo not found"로 변경해준다.
결과

트러블 슈팅 2
@Test
void todo의_user가_null인_경우_예외가_발생한다() {
// given
AuthUser authUser = new AuthUser(1L, "a@a.com", UserRole.USER);
long todoId = 1L;
long managerUserId = 2L;
Todo todo = new Todo();
ReflectionTestUtils.setField(todo, "user", null);
ManagerSaveRequest managerSaveRequest = new ManagerSaveRequest(managerUserId);
given(todoRepository.findById(todoId)).willReturn(Optional.of(todo));
// when & then
InvalidRequestException exception = assertThrows(InvalidRequestException.class, () ->
managerService.saveManager(authUser, todoId, managerSaveRequest)
);
assertEquals("일정을 생성한 유저만 담당자를 지정할 수 있습니다.", exception.getMessage());
}
todo에 매핑되어 있는 user가 null인 경우, InvalidRequestException가 발생하는지 확인하는 테스트 코드이다.

InvalidRequestException가 아니라 NullPointException이 리턴돼서 테스트에 실패하고 있다.
@Transactional
public ManagerSaveResponse saveManager(AuthUser authUser, long todoId, ManagerSaveRequest managerSaveRequest) {
// 일정을 만든 유저
User user = User.fromAuthUser(authUser);
Todo todo = todoRepository.findById(todoId)
.orElseThrow(() -> new InvalidRequestException("Todo not found"));
if (!ObjectUtils.nullSafeEquals(user.getId(), todo.getUser().getId())) {
throw new InvalidRequestException("일정을 생성한 유저만 담당자를 지정할 수 있습니다.");
}
User managerUser = userRepository.findById(managerSaveRequest.getManagerUserId())
.orElseThrow(() -> new InvalidRequestException("등록하려고 하는 담당자 유저가 존재하지 않습니다."));
if (ObjectUtils.nullSafeEquals(user.getId(), managerUser.getId())) {
throw new InvalidRequestException("일정 작성자는 본인을 담당자로 등록할 수 없습니다.");
}
Manager newManagerUser = new Manager(managerUser, todo);
Manager savedManagerUser = managerRepository.save(newManagerUser);
return new ManagerSaveResponse(
savedManagerUser.getId(),
new UserResponse(managerUser.getId(), managerUser.getEmail())
);
}
해당 프로젝트 코드 내에서는 NullPointException을 리턴하는 코드가 없으니까, 의도된 에러 리턴이 아니다.
코드 내 어디선가 null를 참조한 것이다.
테스트 코드에서 todo.user가 null이라고 정의했으므로 todo.getUser()를 참조하는 코드를 찾으면 된다.
if (!ObjectUtils.nullSafeEquals(user.getId(), todo.getUser().getId())) {
throw new InvalidRequestException("일정을 생성한 유저만 담당자를 지정할 수 있습니다.");
}
위 코드에서 todo.getUser().getId() 코드가 실행되기 전에 todo.getUser()가 null 이면 해당 if문 안으로 들어가게 수정하면 된다.
if (todo.getUser() == null || !ObjectUtils.nullSafeEquals(user.getId(), todo.getUser().getId())) {
throw new InvalidRequestException("일정을 생성한 유저만 담당자를 지정할 수 있습니다.");
}
위처럼 수정하면 todo.getUser() == null이 true면 !ObjectUtils.nullSafeEquals(user.getId(), todo.getUser().getId()) 코드를 실행하지 않고 if 문 안으로 들어오기 때문에, NullPointException이 발생하지 않는다.
결과

'Spring > - [시리즈] Spring 프로젝트 되살리기' 카테고리의 다른 글
| Spring 프로젝트 되살리기 8 - 인가(Authorization) 인터셉터 구현 (0) | 2026.07.27 |
|---|---|
| Spring 프로젝트 되살리기 7 - 코드 리팩토링 4 (record, 정적 팩토리 메서드 적용) (0) | 2026.07.26 |
| Spring 프로젝트 되살리기 5 - 코드 리팩토링 3 (@EntityGraph 적용) (0) | 2026.07.25 |
| Spring 프로젝트 되살리기 4 - 코드 리팩토링 2 (@Valid 적용) (0) | 2026.07.25 |
| Spring 프로젝트 되살리기 3 - 코드 리팩토링 1 (중첩 if문 제거 등) (0) | 2026.07.24 |