"테스트 주도 개발 시작하기" - 최범균 저
위 책으로 공부하며 배운 것을 정리한 시리즈
TDD를 공부하며 1장에서(1장은 포스팅하지 않았다.) 암호 강도 측정 기능을 만들었다. 기능을 구현할 때 규칙은 다음과 같았다.
검사할 규칙 3가지
판별 기준
위 요구사항을 만족하는 테스트 코드를 작성한 순서는 다음과 같았다.
위 순서는 다음 규칙에 따라 나온다.
반대로 어려운 경우를 먼저 시작하거나 정상 상황을 먼저 시작하면 구현 과정이 원활하게 진행 되지 않기도 한다. 왜 그럴까?
만약 초반부터 다양한 조합을 검사하는 복잡한 상황을 테스트로 추가하면, 해당 테스트를 통과하기 위해 한 번에 구현해야 할 코드가 많아진다. 예를 들어 다음 순서로 테스트를 만들었다고 하자.
먼저, 대문자 포함 규칙만 충족하는 경우를 테스트하기 위한 코드는 다음과 같이 작성할 수 있다.
@Test
void passOnlyUppercaseThenWeak(){
PasswordStrengthCheck check = new PasswordStrengthCheck();
PasswordStrength result = check.check("abcDef");
assertEquals(PasswordStrength.WEAK, result);
}
이 테스트를 통과시킬 만큼 구현 하려면, 단순히 WEAK를 리턴하면 된다.
public class PasswordStrengthCheck {
public PasswordStrength check(String s) {
return PasswordStrength.WEAK;
}
}
이제 모든 규칙을 충족하는 경우를 테스트하기 위한 코드를 추가한다.
@Test
void passAllReqThenStrong(){
PasswordStrengthCheck check = new PasswordStrengthCheck();
PasswordStrength result = check.check("abcDef12");
assertEquals(PasswordStrength.STRONG, result);
}
이 테스트를 통과시킬려면? "abcDef12" 값이면 아래처럼 STRONG을 리턴하게 해야 할까?
public class PasswordStrengthCheck {
public PasswordStrength check(String s) {
if("abcDef12".equals(s)) return PasswordStrength.STRONG;
return PasswordStrength.WEAK;
}
}
그렇다면 또 다른 값이 들어온다면? 예를 들어 "abcDef12" 가 아닌 "dEf21ab"가 들어온다면? 다른 값이 들어올 때마다 if문을 추가해야 할까? 그럴 순 없다. 좀 더 범용적인 구현이 필요하다. 범용적인 구현은 어떤 것인가? 8글자 이상이면서 대문자와 숫자가 1개 이상 있는 조건을 만들어야 하지 않을까?
그런데 그 다음에는 또 다음 규칙을 위한 구현을 해야 하지 않나? 머리가 아파온다.
한 번에 완벽한 코드를 만들면 좋겠지만, 아쉽게도 모두가 슈퍼 개발자는 아니다. 보통의 개발자는 한 번에 많은 코드를 만들다 보면 자기도 모르게 버그를 만들고, 나중에 버그를 잡기 위해 많은 시간을 허비하게 된다.
가장 구현하기 쉬운 경우부터 시작하면 빠르게 테스트를 통과시킬 수 있다. 보통 수 분에서 십여 분 이내에 구현을 완료해서 테스트를 통과시킬 수 있을 만큼 쉬운 것을 선택한다. 위 예제에서는 다음 중 하나가 쉬울 것 같다.
두 가지 모두 그냥 해당 값을 리턴하면 된다. 첫 번째라면 STRONG, 두 번째라면 WEAK을 리턴하면 된다.
public class PasswordStrengthCheck {
public PasswordStrength check(String s){
return PasswordStrength.STRONG;
}
}
@Test
void passAllReqThenStrong(){
PasswordStrengthCheck check = new PasswordStrengthCheck();
PasswordStrength result = check.check("ab12!@AB");
assertEquals(PasswordStrength.STRONG, result);
}
그 다음은 무엇으로 할까? 어떤 것이 구현하기 쉬울까?
모든 규칙을 충족하지 않는 경우는 지금 구현하기는 어렵다. 앞서서 모든 규칙을 충족하는 경우를 테스트했는데, 이어서 정반대 조건을 구현하려면 결국 모든 규칙을 검사하는 코드를 만들어야 할 것 같다. 한 번에 구현할 것이 너무 많아진다.
한 규칙만 충족하는 경우는, 한 규칙을 검사해서 WEAK을 리턴하면 된다. 두 규칙을 충족하는 경우에도, 충족하지 않는 한 규칙을 검사해서 충족하지 않으면 NORMAL을 리턴하면 된다.
그렇다면, 모든 규칙을 충족하는 경우보다 한 규칙 또는 두 규칙을 충족하는 경우가 더 쉽게 구현할 것이라 예상한다. 그러면 여러 규칙 중에 어떤 규칙이 구현하기 가장 쉬울까? 아마 8글자 이상인지 검사하는 게 쉬울 것 같다. 그래서 다음 테스트를 두 번째로 선택한다.
물론 다음 테스트를 두 번째로 선택해도 구현 난이도는 비슷하게 쉽다.
둘 다 길이가 8글자 이상인지 여부를 판단하는 로직만 구현하면 된다.
한 번에 구현하는 시간이 짧아지면 디버깅할 때에 유리하다. 작성한 코드가 많지 않고 작성 시간도 짧으면 머릿속에 코드에 대한 내용이 생생하게 남아 있으므로, 디버깅할 때 문제의 원인을 빠르게 찾을 수 있다.