1 분 소요

제가 작성한 포스팅중 테스트코드 관련한 포스팅 중에 DB업데이트를 검증하거나 한 코드들이 있습니다.
해당 테스트코드들은 레거시를 감싸기 위한 예제로 작성되어 그렇다는 자그마한 변명을 해봅니다.
리팩토링에 내성이 있는 테스트 코드가 정말 좋은 테스트 코드죠.
만약 리팩토링에 내성이 없는 테스트코드라면 안에 세부구현을 리팩토링을 하면 테스트코드가 전부 깨질 거고 테스트코드를 수정하느라 리팩토링보다 더 많은 시간을 사용해야 할 겁니다.
즉 좋은 테스트코드는 아니죠

그럼 리팩토링에 내성이 있는 테스트 코드를 작성하는 방법에 대한 포스팅입니다.

내부 구현 검증을 피하라

간단합니다. 해당 규칙만 지킨다면 리팩토링에서 테스트가 깨지는 일은 굉장히 줄어듭니다.

먼저 코드로 설명하겠습니다.

//UserService에 메서드
void changeUserName(Long id ,String name){
  User user = userRepository.findById(id);
  user.changeName(name);
  userRepository.update(user);
}



@Test
void changeUsernameTest(){
    //given
    User user = new User("id","userName");
    
    //when
    userService.changeUserName("changeUserName");
    ArgumentCaptor<User> param = ArgumentCaptor.forClass(User.class);
    
    //then
    verify(userRepository, update(1)).update(param.capture());
    assertEquals("changeUserName",param.getValue().getUserName());
}

해당 테스트코드는 User클래스의 이름을 변경하고 DB에 업데이트를 검증하는 테스트코드입니다.
(User 도메인 테스트에서 변경값을 검증하는 방법도 있지만, 서비스에서 한다는 예제로 하겠습니다.)
만약 해당 테스트코드의 내부가 변경될 수 있습니다.
userRepository에서 update가 아니라 save로 바뀐다든지 아니면 update하면서 다른 로직이 추가돼 update가 아닌 다른 메서드를 실행해야한다던지 여러 상황이 있을 수 있습니다.
억지 예제이지만 변경 횟수에 제한이 생겨 userRepository에 changeName이라는 메서드로 업데이트를하면서 변경 횟수에 +1하는 쿼리로 DB에 저장해야 한다 가정하겠습니다.
(더 효율적인 방법이 있지만, 예제를 위해 양해부탁드립니다.)

void changeUserName(Long id ,String name){
  User user = userRepository.findById(id);
  user.changeName(name);
  userRepository.changeName(user);
}

만약 이렇게 변경된다면 기존 테스트코드는 더 성공을 하지 않습니다.
저장한다는 세부구현에 변경이 있거나 리팩토링으로 테스트 코드가 깨진 경우입니다.

해당 경우를 회피하려면 user객체를 변경하는 메서드와 userRepository에 update를 호출하는 메서드를 분리하여 나눠서 테스트하는 방법도 있습니다.
아니면 다음과 같이 직접 DB를 조회해서 확인하는 방법도 있습니다.

@Test
void changeUsernameTest(){
  //given
  User user = new User("id","userName");
  
  //when
  userService.changeUserName("changeUserName");
  
  //then
  //모킹하지않은 진짜 객체
  // userRepository에 업데이트 하는 값을 검사하기보단 업데이트후 DB를 조회하여 검증한다. 즉 메서드내부의 구현을 검증하지않는다.
  User findByid = realUserRepository.findById("id");
  assertEquals("changeUserName",findByid.getUserName());
}

해당 메서드가 끝나서 DB에 업데이트가 된 값을 직접 조회하여 검증하는 것입니다.

어떤 방식을 채택하냐는 테스트 코드 작성자의 마음이지만 리팩토링에 유연하게 대처하려면 해당 메서드의 최종결괏값을 비교하는 것이 더 좋은 테스트 코드라 할 수 있습니다.

댓글남기기