리펙토링에 안전한 테스트 코드

Picbel·2022년 7월 17일
0

Testing Programming

목록 보기
1/3
post-thumbnail

리팩토링에 내성이 있는 테스트 코드가 정말 좋은 테스트 코드죠.
만약 리팩토링에 내성이 없는 테스트 코드라면 안에 세부 구현을 리팩토링을 하면 테스트 코드가 전부 깨질 거고 테스트 코드를 수정하느라 리팩토링보다 더 많은 시간을 사용해야 할 겁니다.
즉 좋은 테스트 코드는 아니죠

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


내부 구현 검증을 피하라

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

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

//UserService에 메서드
public 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에 저장해야 한다 가정하겠습니다.

즉 기존 update메서드는 그대로 유지한채 changeName이란 메서드가 추가된 경우라 생각하시면 되겠습니다.
(더 효율적인 방법이 있지만, 예제를 위해 양해 부탁드립니다.)

public 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에 업데이트가 된 값을 직접 조회하여 검증하는 것입니다.

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

profile
Software Developer

0개의 댓글