[최범균님 테스트 주도 개발] 1 ~ 2장
in 책과 강연 on 최범균님 테스트 주도 개발
1 ~ 2 장
도입부 메모
컴파일 에러를 없애고 실패하는 테스트를 작성한다
테스트 메서드를 통과시킬 만큼의 코드만 작성하는 것이 중요!
성공하는 테스트를 작성한다 : 최소한의 테스트만을 작성한다 !
테스트 코드 중복의 제거
중복을 제거한 뒤에도 테스트 코드의 가독성이 떨어지지 않을 때에만 중복을 제거해야 한다. 중복을 제거한 뒤에 오히려 테스트 코드 관리가 어려워진다면 제거했던 중복을 되돌려야 한다.
레드-그린-리펙터
레드: 실패하는 테스트를 작성한다
그린: 테스트를 간신히 성공할 정도의 구현 코드를 작성한다
리펙터: 구현 코드를 논리적으로 변경하지 않는 정도 선에서 리펙터링한다
테스트가 개발을 주도
테스트 코드가 추가되면서 검증하는 범위가 넓어진다 검증 범위가 넓어지면서 구현도 점점 완성되어 간다! 이렇게 테스트가 개발을 주도해 나간다
지속적인 코드 정리
해당 기능이 온전하게 동작한다는 것을 검증해주는 테스트가 있으면 코드 수정에 대한 심리적 불안감을 줄여준다. 리펙터링을 통한 개선을 원할하게 할 수 있게 도와준다
3장
테스트 코드 작성 순서
- 쉬운 경우에서 어려운 경우로 진행
- 예외적인 경우에서 정상인 경우로 진행
쉬운 경우에서 어려운 경우로 진행
- 수 분에서 십여 분안에 작성 가능한 테스트로
예외적인 경우에서 정상인 경우로 진행
중도에 예외 상황을 반영하면 코드의 구조가 뒤집히거나 중복된 조건문을 추가하는 일이 벌어진다
예외 상황을 처리하는 로직을 생각하지 않아 프로세스가 죽는 일을 미연에 방지할 수 있다
- 시스템 운영 중에
NPE
등이 발생한다면 예외 처리를 하지 않아 프로세스가 죽는 일이 있었다고 한다
- 시스템 운영 중에
완급 조절
“단계적으로 나아가는 연습을 하지 않은 개발자는 조금만 구현이 떠오르지 않아도 진전을 이루지 못하고 막혀 있거나 한 번에 많은 코드를 구현하려고 시도하다 구현에 실패하게 된다.”
“몇 차례 상수를 사용해서 테스트를 통과시키고 뒤에 구현을 일반화하는 과정이 처음에는 지루하게 느껴질 수도 있다. 하지만 이런 연습 과정은 나중에 만들어야 할 코드가 잘 떠오르지 않을 때 점진적으로 구현을 진행할 수 있는 밑거름이 된다.”
- 정해진 값을 리턴
- 값 비교를 이용해서 정해진 값을 리턴
- 다양한 테스트를 추가하면서 구현을 일반화
- 정해진 값을 리턴
PasswordStrengthMeter meter = new PasswordStrengthMeter();
PasswordStrength result = meter.examine("ab12!@A");
Assertions.assertEquals(PasswordStrength.NORMAL, result);
구현 코드
public class PasswordStrengthMeter {
public PasswordStrength meter(String input) {
if("ab12!@A".equals(input)) {
return PasswordStrength.NORMAL;
}
return PasswordStrength.STRONG;
}
}
- 값 비교를 이용해서 정해진 값을 리턴
PasswordStrengthMeter meter = new PasswordStrengthMeter();
PasswordStrength result = meter.examine("ab12!@A");
Assertions.assertEquals(PasswordStrength.NORMAL, result);
PasswordStrength result2 = meter.examine("Ab12!c");
Assertions.assertEquals(PasswordStrength.NORMAL, result2);
구현 코드
if("ab12!@A".equals(input) || "Ab12!c".equals(input)) {
return PasswordStrength.NORMAL;
}
- 다양한 테스트를 추가하면서 구현을 일반화
if(input.length() < 8) {
return PasswordStrength.NORMAL;
}
코드 정리중, 중복 제거에 대해
보통 프로덕션 코드는 중복을 제거하는 쪽으로 리펙터링을 해나가지만 테스트 코드의 경우 중복제거보다도 가독성을 더욱 중요시 여긴다
“테스트 메서드는 스스로 무엇을 테스트하는지 명확하게 설명할 수 있어야 한다”
아래의 경우 중복 제거를 한 테스트 코드이다
@Test
void 만원_납부하면_한달_뒤가_만료일이_됨() {
assertExpiryDate(LocalDate.of(2019, 3, 1),
10_000,
LocalDate.of(2019, 4, 1));
assertExpiryDate(LocalDate.of(2019, 5, 5),
10_000,
LocalDate.of(2019, 6, 5));
}
private void assertExpiryDate(LocalDate billingDate, int payAmount, LocalDate expectedExpiryDate) {
ExpiryDateCalculator calculator = new ExpiryDateCalculator();
LocalDate realExpiryDate = calculator.calculateExpiryDate(billingDate, payAmount);
assertEquals(expectedExpiryDate, realExpiryDate);
}
각 파라미터가 무엇을 뜻하는지를 확인하려면 메서드 구현부인 assertExpiryDate
를 확인해야 하지만
인텔리제이의 힌트로도 알 수 있고
assertExpiryDate
메서드가 길지 않고 파라미터도 세 개 정도이다
따라서 어떤 것을 검증하는 지는 쉽게 알 수 있기 때문에 중복을 제거해도 될 것 같다고 필자는 얘기한다 !