예외 상황을 먼저 테스트해야 하는 이유
- 다양한 예외 상황은 복잡한 if-else 블록을 동반한다.
- 후에 예외 상황을 반영하려면 코드의 구조를 뒤집거나 조건문을 중복해서 추가하는 일이 발생한다.
- 미리 예외 상황을 테스트 하면 예외 상황에 따른 if-else 구조가 미리 만들어지므로 코드 구조가 덜 바뀐다.
- 예외 상황을 처리하지 않아 발생하는 버그를 줄여준다.
완급 조절
한번에 얼마만큼의 코드를 작성 할 것인가?
TDD를 처음 접할 때는 다음 단계에 따라 익히는 것이 추천된다.
- 정해진 값을 리턴
- 값 비교를 이용해서 정해진 값을 리턴
- 다영한 테스트를 추가하면서 구현을 일반화
예를 들어 암호 강도 측정 기능에서 길이가 8글자 미만이지만 나머지 규칙은 충족하는 상황을 위 단계를 밟아 진행해보자. 먼저 다음 테스트 코드를 추가했다.
@Test
void passTwoReqExceptLengthThenNormal2(){
PasswordStrengthCheck check = new PasswordStrengthCheck();
PasswordStrength result = check.check("ab12!@A");
assertEquals(PasswordStrength.NORMAL, result);
}
딱 이 테스트를 통과할 만큼만 코드를 작성하자.
public class PasswordStrengthCheck {
public PasswordStrength check(String s){
if("ab12!@A".equals(s)) {
return PasswordStrength.NORMAL;
}
return PasswordStrength.STRONG;
}
}
상수를 비교해서 테스트를 통과시켰다. 다음으로 동일한 조건을 검증하기 위한 테스트를 추가한다.
@Test
void passTwoReqExceptLengthThenNormal2(){
PasswordStrengthCheck check = new PasswordStrengthCheck();
PasswordStrength result = check.check("ab12!@A");
assertEquals(PasswordStrength.NORMAL, result);
PasswordStrength result2 = check.check("Ab12!c");
assertEquals(PasswordStrength.NORMAL, result2);
}
새로 추가한 테스트를 통과시키려면 다음과 같이 값 비교를 추가하면 된다.
public class PasswordStrengthCheck {
public PasswordStrength check(String s){
if("ab12!@A".equals(s)) || "Ab12!c".equals(s)) {
return PasswordStrength.NORMAL;
}
return PasswordStrength.STRONG;
}
}
이번에도 상수를 이용해서 통과시켰다. 다음 차례는 상수를 제거하고 일반화하는 것이다. 다음은 위 코드에서 상수를 제거하고 일반화한 결과이다.
public class PasswordStrengthCheck {
public PasswordStrength check(String s){
if(s.length() < 8) {
return PasswordStrength.NORMAL;
}
return PasswordStrength.STRONG;
}
}
몇 차례 상수를 사용해서 테스트를 통과시키고 뒤에 구현을 일반화하는 과정이 처음에는 지루하게 느껴질 수도 있지만, 이런 연습 과정은 나중에 만들어야 할 코드가 잘 떠오르지 않을 때 점진적으로 구현을 진행할 수 있는 밑거름이 된다.
TDD가 익숙해지면 상황에 따라 구현 속도를 조절할 수 있게 된다. 단순 덧셈이나 길이 비교와 같은 명백한 구현은 상수를 사용하지 않고 바로 구현하고 한 번에 구현을 시도했는데 잘 안되면 한발 물러서서 천천히 단계를 밟아나가자.
지속적인 리팩토링
테스트 통과 후에는 리팩토링을 진행한다. 매번 리팩토링을 진행해야 하는 건 아니지만 적당한 후보가 보이면 진행하는 것이 좋다. 코드 중복은 대표적인 리팩토링 대상이다. 코드가 길어지면 메서드 추출과 같은 기법을 사용해서 메서드 이름으로 코드의 의미를 표현할 수 있다.
TDD를 진행하는 과정에서 지속적으로 리팩토링을 진행하면 코드 가독성 또한 높아진다. 이는 유지보수에 도움이 된다.
- 일단 동작하는 코드를 만드는 능력은 중요하다. 코드가 동작하지 않으면 아무것도 소용없다.
- 하지만 소프트웨어의 생존 시간이 길어질수록 소프트웨어를 지속적으로 개선해야 한다.
- 코드 변경이 어려우면 변화하는 요구를 제때 반영할 수 없게 되며 이는 소프트웨어의 생존과 직결된다. 따라서 코드를 잘 변경할 수 있는 능력 또한 매우 중요하다.
- 코드를 잘 변경하려면 변경하기 쉬운 구조를 가져야 하는데, 이를 위한 것이 바로 리팩토링이다.
테스트 대상 코드의 리팩토링 시점
- 상수를 변수로 바꾸거나 변수 이름을 변경하는 것과 같은 작은 리팩토링은 발견 시 바로 실행한다.
- 메서드 추출과 같이 메서드의 구조에 영향을 주는 리팩토링은 큰 틀에서 구현 흐름이 눈에 들어오기 시작한 뒤에 진행한다.
- 구현 초기에는 아직 구현의 전반적인 흐름을 모를 수 있기 때문에 큰 리팩토링을 진행하면 코드 구조를 잘못 잡을 가능성이 있다.
- 따라서 코드 구조가 잘못 되거나 다음 구현이 어려워지면 구현을 멈추고 리팩토링을 되돌린 후 다음 테스트를 진행한다.
- 코드의 의미, 구조가 더 명확해지면 그때 다시 리팩토링을 시도한다.
'Book > 테스트 주도 개발 시작하기' 카테고리의 다른 글
TDD - 테스트 작성 순서 연습(1) (0) | 2023.12.20 |
---|---|
TDD - 테스트 코드 작성 순서 (1) (0) | 2023.12.20 |