Justin의 개발 로그

구현하기 쉬운 테스트부터 시작하기 

  • 구현이 쉬운 테스트 케이스부터 시작한다.
  • 한 번에 많은 부분을 수정해서 테스트하지 않는다.
  • 테스트에 통과한 즉시 리팩토링 한다.
    •  새로 추가한 코드에서 리팩토링이 필요한 부분이 있는지 즉시 검토한다.
    • 코드가 추가되고, 테스트를 통과(버그x)할 때마다 리펙토링을 하면, 많은 코드를 한꺼번에 리팩토링해서 발생하는 side effect을 최소화 할 수 있다.

예외 테스트 케이스에서 정상적인 케이스로 테스트 진행

  • 다양한 예외 상황은 복잡한 if-else 블록을 양산할 수 있다.
  • 예외 상황을 고려하지 않고, 개발된 코드에 나중에 예외 상황을 반영하려면 코드의 구조를 바꿔야 하는 경우가 생기거나, 코드 중간에 예외 사항을 처리하기 위한 if 블록이 추가되면서 복잡도가 증가하게 된다.
  • 초반에 예외사항을 테스트하면 위의 상황을 만날 가능성을 줄여준다.
  • TDD를 하는 동안 예외 상황을 먼저 고민하고 테스트에 반영하면, 예외 상황을 처리하지 않아 발생하는 버그를 줄여준다.

코드 작성 순서

  • 정해진 값을 리턴
  • 값 비교를 이용해서 정해진 값을 리턴
  • 다양한 테스트를 추가하면서 구현을 일반화 
@Test
void 비밀번호가7자리이하_숫자대문자포함() {
  PasswordStrengthMeter meter = new PasswordStrengthMeter();
  PasswordStrength result = meter.meter("ab12!@A");  //8자리 제외한 2조건 충족
  
  assertEquals(PasswordStrength.NORMAL, result);
}


//로직 구현 1차 - 즉시 테스트
public class PasswordStrengthMeter {
  public PasswordStrength meter(String s) {
    return PasswordStrength.NORMAL;
  }
}

//로직 구현 2차 - 즉시 테스트
public class PasswordStrengthMeter {
  public PasswordStrength meter(String s) {
    if("ab12!@A".equals(s))
      return PasswordStrength.NORMAL;
    
    return PasswordStrength.STRONG;
  }
}

테스트 케이스 추가 

@Test
void 비밀번호가7자리이하_숫자대문자포함() {
  PasswordStrengthMeter meter = new PasswordStrengthMeter();
  PasswordStrength result = meter.meter("ab12!@A");  //8자리 제외한 2조건 충족
  assertEquals(PasswordStrength.NORMAL, result);

  PasswordStrength result2 = meter.meter("Ab12!@c");  //8자리 제외한 2조건 충족
  assertEquals(PasswordStrength.NORMAL, result2);

}


//로직 구현 3차 
public class PasswordStrengthMeter {
  public PasswordStrength meter(String s) {
    if("ab12!@A".equals(s) || "Ab12!@c".equals(s))
      return PasswordStrength.NORMAL;
    
    return PasswordStrength.STRONG;
  }
}

//로직 구현 4차 - 상수 비교를 걷어내고, 가장 쉬운 로직으로 변경
//   이때 모든 로직을 완벽하게 만들려는 조바심을 낼 필요는 없다.
public class PasswordStrengthMeter {
  public PasswordStrength meter(String s) {
    if(s.length() < 8)
      return PasswordStrength.NORMAL;
    
    return PasswordStrength.STRONG;
  }
}


정해진 값 리턴 -> 상수 비교 정해진 값 리턴 -> 일반화된 코드 구현

-> 테스트 케이스 추가 -> 정해진 값 리턴 -> .... 반복

  • 위의 과정을 반복하는 과정에서 코드에 필요한 로직들이 채워지고, 리팩토링이 되면서, 구조가 잡혀 나간다.

 

지속적인 리팩토링

  • 테스트를 통과한 후에는 리팩토링을 진행한다.
  • 반드시 매번 리팩토링을 해야 할 필요는 없지만, 적당한 후보(냄새나는 코드)가 보이면 리팩토링을 진행한다.
  • 테스트 코드도 적당한 후보(냄새나는 코드 - 특히 반복)가 보이면 리팩토링을 진행한다.
  • 지속적인 리팩토링은 코드 가독성을 높이고, 수정 요청이 있을 때 변경할 코드를 빠르게 찾을 수 있다.
  • 객체지향 원칙(SOLID 5)을 준수하는 코드 수준을 유지하면, 수정 요청 사항이 있을 때 변경하는 코드가 명확해 진다.

리팩토링 순서 

  • 매직넘버(1, 999, "ABC")를 변수로 변경
  • 변수를 상수(const, final)로 변경
  • 변수 이름 또는 메서드 이름의 변경은 즉시 리팩토링 한다.
  • 메뉴얼로 리팩토링하지 말고, IntelliJ의 리팩토링 기능을 최대한 활용한다. (실수 방지, 연관 코드 자동 변환)
  • 메서드 분리와 같은 리팩토링은 큰 틀에서 구현 흐름(로직의 전개)가 눈에 들어오기 시작한 뒤에 진행한다.
  • 전반적인 흐름이 잡히지 않은 상태에서 메서드 분리를 진행하면 코드 구조를 잘 못 잡을 가능성이 있다.
  • 리팩토링 이후에 테스트가 통과되지 않거나, 구현을 더는 진행이 안되는 상황이 오면, 리팩토링을 취소(되돌림)한다.
  • 리팩토링을 취소해서 코드를 원복한 경우에도 테스트를 진행한다. 그런 뒤 코드의 의미나 구조가 더 명확해지면 그때 다시 리팩토링을 시도한다.

 

 

 

 

profile

Justin의 개발 로그

@라이프노트

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!