본문 바로가기
Spring/- [시리즈] Spring 프로젝트 되살리기

Spring 프로젝트 되살리기 6 - 유닛 테스트 코드 고치기

by 정구정구 2026. 7. 25.

이전 이야기

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() == nulltrue!ObjectUtils.nullSafeEquals(user.getId(), todo.getUser().getId()) 코드를 실행하지 않고 if 문 안으로 들어오기 때문에, NullPointException이 발생하지 않는다.

 

결과