드디어 테스트 주도 개발에 대한 핵심에 돌입한다.
📖 원본이 궁금하면 이규원 님의 TDD 수강하는 걸 추천드립니다. 👍
테스트 주도 개발
이전 시간에 했던 테스트 우선 개발과 비슷하다.
명세를 준수하고 일정 수준의 품질을 유지하며 코드를 만들어간다.
테스트 주도 개발 절차
테스트 주도 개발은 3가지 단계로 반복적으로 사이클이 돌아간다.
1. RED - 실패하는 테스트 추가
2. GREEN - 테스트를 통과하기 위한 구현
3. REFACTOR - 통과한 테스트의 구현 설계를 개선, 기존 테스트가 통과해야한다
테스트 실패 (RED)
1. 하나의 요구사항을 검증하는 테스트를 추가한다.
2. 추가된 테스트가 실패하는지 확인한다.
-> 운영코드 작성 후 통과하는걸 봐야 이 운영코드가 테스트에 검증력이 있다는걸 알게 된다.
-> 운영코드 변경 전 테스트가 실패하는지 확인해야한다. 잘못된 테스트로 운영코드에 관계없이 실패할 수도 있다.
테스트 성공 (GREEN)
1. 테스트가 성공하도록 코드를 구현한다.
2. 테스트 성공은 요구사항을 만족한다는 의미
3. 최소한의 코드로 성공해야한다.
-> 요구사항을 만족하기 위한 최소한의 뼈대만 세우자, 살 붙이는건 중요도가 낮다
개선 (REFACTOR)
1. 코드베이스 정리
2. 구현 설계 개선
-> 인터페이스가 아닌 구현 설계 개선을 의미
3. 리팩터링 후 모든 테스트 성공을 전제
켄트 벡의 설계 규칙
1. 통과하는 테스트 - 설계의 가장 중요
2. 의도 노출 - 가독성과 비슷
2. 중복 제거 - 코드 변경에 대응하기 쉬움
4. 불필요한 요소 제거 - 위의 3가지 규칙에 불필요한 요소는 제거
주의점 : 의도 노출과 중복 제거는 서로 충돌할 수 있어서 같은 우선순위에 둔다고 함. 켄트 벡의 블로그 참고
TDD는 낯설지 않다
흔히 이슈 발생을 생각해보면 TDD와 흡사하다는 걸 깨달을 수 있다.
버그 발생(제보) -> 버그 확인 -> 코드 수정 -> 버그 재확인 -> 작업완료
TDD 비용
위의 그래프를 보면 TDD 를 적용하는게 모두 좋은건 아니다.
t1 시점이 오기전까지 추가적인 기능 없이 운영이 된다면 TDD 미사용이 더 비용이 낮고 시간도 아낀다.
t1 시점이 와도 TDD 가 좋은건 아니다. 이전 격차만큼 t2 까지는 TDD 미사용이 더 비용이 저렴하다
지속적인 기능 확장이 되면 TDD 미사용이 엄청나게 개발 속도가 느려지게 된다.
원인은 이전 기능에 대한 테스트가 전혀 없기 때문에 기능을 모두 재검증해야한다. (경험담)
그에 반해 TDD는 테스트 자동화로 손쉽게 피드백을 받아 확인할 수 있다.
개발자라면 지속적인 기능 확장이 있는지 파악하고 TDD를 적용할지 판단해야한다.
하지만 TDD 도구가 발달하여 t1, t2 시점이 더 앞으로 땡겨진다면 TDD 도입을 적극적으로 생각해보자.
TDD 실습
이전에 했던 공백, 금지어를 계속 진행하겠다.
사용자가 절삭도 추가해달라 요청했다. 테스트 실패(RED)를 진행하자.
@Test
@DisplayName("절삭 테스트")
public void cuttingTest() throws Exception {
String source = " hello world";
String expected = "hello world";
boolean result = blankReplace.refineText(source).equals(expected);
assertEquals(result, true);
}
이제 가장 빠른 결과를 받기위해 trim() 을 사용해서 진행해보자
public String refineText(String source, List<String> bannedWords) {
source = source.trim();
return maskBannedWords(compactWhiteSpaces(normalizeWhiteSpaces(source)), bannedWords);
}
테스트 성공(GREEN) 되었다.
마지막 단계인 리팩토링을 하자
public String refineText(String source, List<String> bannedWords) {
return maskBannedWords(compactWhiteSpaces(normalizeWhiteSpaces(source.trim())), bannedWords);
}
재대입을 지우고 곧바로 trim() 보내보자
trim() 하는 과정도 메소드로 추출하자
public String refineText(String source, List<String> bannedWords) {
return maskBannedWords(compactWhiteSpaces(normalizeWhiteSpaces(trimWhiteSpaces(source))), bannedWords);
}
private String trimWhiteSpaces(String source) {
return source.trim();
}
지금까지 간단히 TDD 에 대해 알아보았다.
다음에는 TDD 의 이점에 대해 정리하겠다.
'교육 및 인강 > 이규원의 현실 세상의 TDD' 카테고리의 다른 글
이규원님의 현실 세상의 TDD 깊은 곳, 1편 : 인터페이스와 구현 (0) | 2021.05.19 |
---|---|
이규원님의 현실 세상의 TDD 기초, 8편 : 프로그래머 피드백 (0) | 2021.04.16 |
이규원님의 현실 세상의 TDD 기초, 6편 : 정리된 코드(리팩토링) (0) | 2021.04.14 |
이규원님의 현실 세상의 TDD 기초, 5편 : 테스트 우선 개발 (0) | 2021.04.13 |
이규원님의 현실 세상의 TDD 기초, 4편 : 단위테스트 (2) | 2021.04.13 |