테스트 코드 복습
TDD
이번 시간에는 TDD가 무엇인지 알아보고 TDD의 개발 흐름을 대략적으로 살펴보겠습니다.
그리고 TDD의 장/단점을 살펴보면서 TDD를 적용하려면 어떻게 하는 게 좋을지 생각해 보는 시간을 가져 보겠습니다.
[기본] TDD란?
TDD(Test Driven Development)란?
- TDD(Test Driven Development, 테스트 주도 개발)란 무엇일까요?
테스트 주도 개발이라는 용어에서 알 수 있듯이 대략적으로 의미를 생각해 봐도 개발을 진행하는 데 있어서 테스트가 왠지 중요한 역할을 할 것이라는 생각이 듭니다.
우리가 Spring Data JDBC에서 대략적으로 학습했던 DDD(Domain Driven Design)를 떠올려 보세요.
DDD는 도메인 중심의 설계 기법입니다. 도메인 모델이 애플리케이션 개발에 있어 핵심적인 역할을 하기 때문에 도메인 모델 없이는 애플리케이션도 있을 수 없습니다.
도메인 모델이 DDD의 중심에 서 있다면 TDD에는 테스트가 그 중심에 서 있습니다.
그런데 테스트 주도 개발이라는 건 도대체 어떤 의미일까요?
TDD의 개념을 한마디로 요약하자면 ‘테스트를 먼저 하고 구현은 그다음에 한다’로 요약할 수 있습니다.
구현도 안 했는데 테스트를 어떻게 할 수 있는지 참 신기하기도 합니다.
TDD가 아닌 전통적인 개발 방식
우리가 어떤 서비스 애플리케이션을 개발할 때 개발 절차는 일반적으로 다음과 같습니다. (고객의 요청으로 고객이 원하는 시스템을 구축하는 것이 아니라 서비스 제공 기업에서 불특정 다수의 회원에게 제공하는 서비스 애플리케이션을 의미합니다.)
- 서비스 제작에 관여하는 이해 당사자(기획자, 프론트엔드 개발자, 백엔드 개발자, 웹 디자이너 등)가 모여 서비스에 대한 콘셉트과 해당 콘셉트에 따른 요구 사항을 지속적으로 수집합니다.
- 수집된 요구 사항에 맞춰 서비스를 화면으로 제공하기 위한 UI(User Interface)를 설계하면서 구체적인 기능 요구 사항들을 정의합니다.
- 프론트엔드 개발자는 기능 요구 사항과 UI를 통해 프론트엔드 측 개발을 진행하고, 웹 디자이너는 화면을 디자인하며, 백엔드 개발자는 역시 기능 요구 사항에 맞춰 백엔드 애플리케이션을 디자인합니다.
1번, 2번, 3번 중에서 일반적으로 3번의 과정을 진행하면서 요구 사항이 수정되기도 하고 그에 따라 UI가 변경되기도 하며, 프론트엔드와 백엔드 측 설계가 변경되는 경우가 많습니다.
앞에서 얘기한 개발 절차는 일반적인 흐름이긴 하지만 애자일 방식으로 1주 ~ 3주 단위로 기획, 설계, 구현을 반복적으로 빠르게 진행하면서 애플리케이션을 완성하는 방식을 도입하는 기업도 많이 있음을 참고하면 좋을 것 같습니다.
애자일 개발 방식에 대해서 더 알아보고 싶다면 아래 [심화 학습]을 참고하세요.
3번 과정에서 백엔드 개발자의 개발 흐름은 일반적으로 다음과 같습니다.
- 이해 당사자들 간에 수집된 요구 사항과 설계된 화면(UI 설계서 등) 등을 기반으로 도메인 모델을 도출합니다.
- 도출된 도메인 모델을 통해 클라이언트의 요청을 받아들이는 엔드포인트와 비즈니스 로직, 데이터 액세스를 위한 클래스와 인터페이스 등을 설계해서 큰 그림을 그려봅니다.
- 클래스 설계를 통해 애플리케이션에 대한 큰 그림을 그려보았다면 클래스와 인터페이스의 큰 틀을 작성합니다.
- 클래스와 인터페이스의 큰 틀이 작성되었다면 클래스와 인터페이스 내에 메서드를 정의하면서 세부 동작을 고민하고, 코드로 구현합니다.
- 해당 메서드의 기능 구현이 끝났다면 구현한 기능이 잘 동작하는지 테스트합니다.
- 테스트에 문제가 발생한다면 구현한 코드를 디버깅하면서 문제의 원인을 찾습니다.
위 백엔드 개발자의 개발 흐름 중, TDD 관점에서 두드러지는 점 한 가지는 3번 ~ 6번의 과정에서 구현이 먼저고, 테스트가 나중이라는 점입니다.
3번, 4번 과정에서 구현을 먼저 하고 5번 과정에서 테스트를 진행하는 것을 볼 수 있습니다.
여기서 백엔드 개발자의 개발 흐름을 예로 들었지만 프론트엔드 개발자든 백엔드 개발자든 간에 애플리케이션 개발 흐름은 ‘선 구현, 후 테스트’가 일반적인 흐름입니다.
구현도 하지 않았는데 테스트를 한다는 것 자체가 말이 안 되는 상황이라고 생각되는 건 어쩌면 당연한 걸 테니까요.
이처럼 말이 안 되는 걸 말이 되게 하는 개발 방식이 바로 TDD입니다.
TDD 방식으로 개발하며 TDD의 특성 알아보기
그렇다면 도대체 TDD 방식으로 개발을 어떻게 진행하는 걸까요?
하나의 기능을 구현해 보면서 TDD의 개발 방식이 어떤 형태로 이루어지는지 살펴보도록 하겠습니다.
‘⭐’가 붙은 내용은 우리가 하나의 기능을 TDD 방식으로 구현해 보면서 발견되는 TDD의 특성이라는 걸 기억하세요.
마지막에 한꺼번에 정리하도록 하겠습니다.
우리가 애플리케이션 보안에 대한 학습은 아직 하지 않았기 때문에 커피 주문 샘플 애플리케이션에서 회원 등록 시, 로그인 인증을 위한 패스워드 정보는 빠져 있긴 하지만 어쨌든 회원 등록 시 입력하는 로그인 인증용 패스워드의 유효성을 검증하는 기능을 TDD 방식으로 개발해 보겠습니다.
먼저 간단히 우리가 구현할 패스워드 유효성 검증에 통과하는 조건은 다음과 같습니다.
- 패스워드 길이는 8 ~ 20 사이의 길이(length)여야 한다.
- 패스워드는 알파벳 소문자 + 알파벳 대문자 + 숫자 + 특수 문자 형태로 구성되어야 한다.
- 알파벳 대/소문자와 숫자를 제외한 모든 문자는 특수문자라고 가정합니다.
위 조건을 모두 만족해야지만 패스워드 유효성 검증에서 통과할 수 있습니다.
먼저 패스워드 유효성 검증을 수행할 테스트 클래스와 테스트 케이스의 이름을 정합니다.
package com.springboot.tdd;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class PasswordValidatorTest {
@DisplayName("패스워드 유효성 검증 테스트: 모든 조건에 만족")
@Test
public void validatePassword() {
}
}
[코드 3-200] 테스트 클래스와 테스트 케이스 이름 정하기
패스워드의 유효성 검증을 테스트할 테스트 클래스의 이름과 테스트를 수행할 메서드의 이름을 적절하게 정했습니다.
이 상태에서 테스트 케이스를 실행해 봅니다.
구체적인 테스트 코드가 전혀 없기 때문에 일단 테스트 케이스의 실행 결과는 “passed”입니다.
우리는 이제 테스트를 본격적으로 수행할 수 있는 적절한 환경을 갖추게 되었습니다.
✅ 모든 유효성 검증 조건을 만족하는 테스트
다음 단계로 넘어가보겠습니다.
[그림 3-79] 테스트 케이스의 컴파일 에러 단계
그림 3-79는 완성된 테스트 케이스입니다.
테스트 데이터는 패스워드 유효성 검증에 통과하는 문자열로 먼저 입력했습니다.
이렇게 하면 유효성 검증에 통과하지 못하는 실패한 테스트 케이스를 성공하는 테스트로 단계적으로 수정하기 때문에 모든 조건에 만족하기 위해 한꺼번에 너무 많은 기능을 구현하지 않고 점진적으로 수정해 갈 수 있습니다.
⭐ 모든 조건에 만족하는 테스트를 먼저 진행한 뒤에 조건에 만족하지 않는 테스트를 단계적으로 진행하면서 실패하는 테스트를 점진적으로 성공시켜 갑니다.
그런데 테스트 케이스를 실행해 봤자 테스트에 실패할 것은 뻔한 사실입니다.
아직 존재하지 않는 PasswordValidator 클래스 때문에 컴파일 에러가 날 테니까요.
그래도 테스트 케이스를 실행해 봅니다. 실행 결과는 공식적으로 “failed”가 뜨는 건 아니지만 컴파일 에러가 나므로 “failed”나 마찬가지입니다.
우선 컴파일 에러부터 해결해 봅시다.
해결하는 방법은 단순합니다. 존재하지 않는 PasswordValidator 클래스를 생성하면 됩니다.
package com.springboot.tdd;
public class PasswordValidator {
}
[코드 3-201] 아직 비어 있는 PasswordValidator 클래스
[그림 3-80] 테스트 케이스의 컴파일 에러 단계
코드 3-201과 같이 존재하지 않는 PasswordValidator 클래스를 생성함으로써 클래스가 존재하지 않아서 발생하는 컴파일 에러는 해결될 것입니다.
그런데 이 상태에서 테스트 케이스를 실행해 봤자 [그림 3-80]과 같이 아직 빨간 글자가 사라지지 않았기 때문에 당연히 컴파일 에러가 발생할 것이라고 예상할 수 있습니다.
하지만 그래도 테스트 케이스를 실행해 봅니다.
PasswordValidator 클래스에 validate() 메서드가 없어서 발생하는 에러를 먼저 해결해 보겠습니다.
package com.springboot.tdd;
public class PasswordValidator {
public void validate(String password) {
}
}
[코드 3-202] PasswordValidator 클래스에 validate() 메서드 추가
IntelliJ의 메서드 생성 기능을 이용해서 코드 3-202와 같이 메서드 body가 비어있는 validate() 메서드를 추가했습니다.
[그림 3-81] 테스트 케이스의 컴파일 에러 수정 완료
PasswordValidator 클래스에 validate() 메서드를 추가한 뒤에 \[그림 3-81]과 같이 드디어 모든 빨간 줄과 빨간색 표시가 사라졌습니다.
이 상태에서 테스트 케이스를 실행하면 이제 드디어 첫 번째 “passed”를 볼 수 있습니다.
TDD의 개발 방식을 어렴풋이나마 짐작할 수 있을까요?
⭐ “failed”인 테스트 케이스를 지속적으로 그리고 단계적으로 수정하면서 테스트 케이스 실행 결과가 “passed”가 되도록 만들고 있습니다.
⭐ 지금껏 작성한 코드의 양은 많지 않습니다. 여기서 TDD의 또 하나의 특성을 알 수 있습니다. TDD에서는 테스트가 “passed” 될 만큼의 코드만 우선 작성합니다.
✅ (알파벳 소문자 + 알파벳 대문자 + 숫자 + 특수 문자) 조건에서 특수 문자가 빠진 경우 테스트
package com.springboot.tdd;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
public class PasswordValidatorTest {
@DisplayName("모든 조건에 만족")
@Test
public void validatePasswordAllCriteria() {
// given
String password = "Abcd1234!";
// when
PasswordValidator validator = new PasswordValidator();
Executable executable = () -> validator.validate(password);
// then
assertDoesNotThrow(executable);
}
// (1)
@DisplayName("특수 문자 포함 안됨 테스트")
@Test
public void validatePasswordWithoutSpecialCharacter() {
// given
String password = "Abcd1234";
// when
PasswordValidator validator = new PasswordValidator();
Executable executable = () -> validator.validate(password);
// then
assertDoesNotThrow(executable);
}
}
[코드 3-203] 특수문자가 없는 패스워드 테스트
이제 코드 3-203처럼 테스트 케이스 하나를 더 추가합니다.
이번에는 유효한 패스워드가 되는 조건 중에서 ‘특수 문자 포함’이라는 조건에 만족하지 않는 패스워드를 테스트하겠습니다.
현재 상태에서 코드 3-203의 두 개의 테스트 케이스를 모두 실행하면 실행 결과는 “passed”일 것입니다.
PasswordValidator 클래스에 패스워드를 검증하는 조건이 하나도 없으니까요.
이 상태에서 모든 테스트 케이스가 실패하는 조건은 무엇일까요?
패스워드가 어떤 조건에 만족하든지 간에 무조건 Exception을 던지게 하면 될 것입니다.
package com.springboot.tdd;
public class PasswordValidator {
public void validate(String password) {
throw new RuntimeException("Invalid password");
}
}
[코드 3-204] PasswordValidator의 validate()에 Exception을 던지도록 수정
PasswordValidator.validate() 메서드가 무조건 RuntimeException을 던지도록 수정했습니다.
이 상태에서 코드 3-203의 모든 테스트 케이스를 실행하면 두 개의 테스트 케이스 모두 “failed” 됩니다.
이제 패스워드에 특수 문자를 포함하지 않는 경우에만 “failed” 되도록 기능을 수정해 봅시다.
package com.springboot.tdd;
public class PasswordValidator {
public void validate(String password) {
// (1)
boolean containSpecialCharacter =
password.chars()
.anyMatch(ch -> !(Character.isDigit(ch) || Character.isAlphabetic(ch)));
// (2)
if (!containSpecialCharacter) {
throw new RuntimeException("Invalid password");
}
}
}
[코드 3-205] 특수 문자를 포함하지 않는 경우만 예외를 던지도록 수정
코드 3-205에서는 (1)과 같이 특수 문자를 포함하고 있는지의 여부를 체크한 뒤에, (2)에서 특수 문자를 포함하고 있지 않을 경우에만 예외를 던지도록 기능을 수정했습니다.
테스트를 다시 실행하면 validatePasswordWithoutSpecialCharacter() 테스트 케이스는 특수 문자가 없기 때문에 “failed”가 됩니다.
validatePasswordWithoutSpecialCharacter()의 실행 결과를 “passed”가 되도록 해 봅시다.
package com.springboot.tdd;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
public class PasswordValidatorTest {
...
...
@DisplayName("특수 문자 포함 안됨")
@Test
public void validatePasswordWithoutSpecialCharacter() {
// given
String password = "Abcd1234&!"; // (1)
// when
PasswordValidator validator = new PasswordValidator();
Executable executable = () -> validator.validate(password);
// then
assertDoesNotThrow(executable);
}
}
[코드 3-206] 특수 문자가 포함되도록 테스트 데이터 수정
코드 3-206의 (1)과 같이 특수 문자가 포함이 되도록 패스워드를 수정했습니다.
이제 클래스 레벨에서 테스트 케이스를 실행시키면 모든 테스트의 실행 결과는 “passed”입니다.
✅ PasswordValidator 클래스 리팩토링
모든 테스트 케이스는 성공을 했는데, 다른 조건을 만족하는 로직이 추가되면 PasswordValidator 클래스의 유효성 검증 로직 코드가 깔끔하지 않을 것 같다는 생각이 들었습니다.
정규 표현식을 사용하면 더 깔끔할 것 같아서 정규 표현식을 사용하여 패스워드의 유효성 검사를 진행하도록 리팩토링 하기로 결정했습니다.
package com.springboot.tdd;
import java.util.regex.Pattern;
public class PasswordValidator {
public void validate(String password) {
// (1)
if (!Pattern.matches("(?=.*\\\\W)(?=\\\\S+$).+", password)) {
throw new RuntimeException("Invalid password");
}
}
}
[코드 3-207] 특수 문자가 포함 여부를 정규 표현식으로 검증하도록 수정
코드 3-207과 같이 정규 표현식으로 체크하도록 수정하니 코드가 깔끔해졌습니다.
수정된 코드가 잘 동작하는지를 체크하기 위해서 전체 테스트 케이스를 다시 실행합니다.
“passed”입니다.
그런데 수정된 코드가 잘 동작하는지 여부를 정확하게 테스트하기 위해서는 특수 문자를 포함하지 않은 패스워드를 한번 더 테스트해서 “failed”인지 확인하는 것이 좋습니다.
package com.springboot.tdd;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
public class PasswordValidatorTest {
...
...
@DisplayName("특수 문자 포함 안됨 테스트")
@Test
public void validatePasswordWithoutSpecialCharacter() {
// given
String password1 = "Abcd1234&!";
String password2 = "Abcd1234"; // (1)
// when
PasswordValidator validator = new PasswordValidator();
Executable executable1 = () -> validator.validate(password1);
Executable executable2 = () -> validator.validate(password2); // (2)
// then
assertDoesNotThrow(executable1);
assertDoesNotThrow(executable2); // (3)
}
}
[코드 3-208] 특수 문자가 포함되지 않은 테스트 데이터를 추가한 후 재검증
코드 3-208의 (1)과 같이 특수 문자를 포함하지 않은 패스워드를 테스트 데이터로 추가한 후, (2)에서 동작을 정의하고 (3)에서 추가된 테스트 데이터에 대한 검증을 실시합니다.
테스트 결과는 “failed”입니다.
이제 특수 문자가 포함되지 않은 패스워드는 유효성 검증에서 실패한다는 것을 확실히 검증할 수 있게 되었습니다.
이제 아래와 같이 유효한 패스워드인지를 검증하는 다음 조건에 대해 앞에서 특수 문자 포함 여부를 검증한 방식과 마찬가지로 테스트와 검증, 리팩토링 단계를 반복해서 진행하면 됩니다.
- 패스워드가 특수문자를 포함하면서 알파벳 소문자를 포함하는지 테스트, 검증, 리팩토링 단계 반복
- 패스워드가 특수문자 + 알파벳 소문자 + 알파벳 대문자를 포함하는지 테스트, 검증, 리팩토링 단계 반복
- 패스워드가 특수문자 + 알파벳 소문자 + 알파벳 대문자 + 숫자를 포함하는지 테스트, 검증, 리팩토링 단계 반복
- 패스워드가 특수문자 + 알파벳 소문자 + 알파벳 대문자 + 숫자를 포함하면서 8 ~ 20 길이(length)를 만족하는지 테스트, 검증, 리팩토링 단계 반복
TDD의 개발 방식이 조금은 이해가 될까요?
⭐ TDD의 개발 방식은 ‘실패하는 테스트 → 실패하는 테스트를 성공할 만큼의 기능 구현 → 성공하는 테스트 → 리팩토링 → 실패하는 테스트와 성공하는 테스트 확인’이라는 흐름을 반복합니다.
TDD의 특징 정리
앞에서 간단한 기능을 구현해 보면서 살펴보았던 TDD의 특징을 다시 한번 정리해 보겠습니다.
- TDD는 모든 조건에 만족하는 테스트를 먼저 진행한 뒤에 조건에 만족하지 않는 테스트를 단계적으로 진행하면서 실패하는 테스트를 점진적으로 성공시켜 갑니다.
- TDD는 테스트 실행 결과가 “failed”인 테스트 케이스를 지속적으로 그리고 단계적으로 수정하면서 테스트 케이스 실행 결과가 “passed”가 되도록 만들고 있습니다.
- TDD는 테스트가 “passed” 될 만큼의 코드만 우선 작성합니다.
- TDD는 ‘실패하는 테스트 → 실패하는 테스트를 성공할 만큼의 기능 구현 → 성공하는 테스트 → 리팩토링 → 실패하는 테스트와 성공하는 테스트 확인’이라는 흐름을 반복합니다.
앞에서 TDD 방식으로 진행한 패스워드 유효성 검증 기능의 구현에 대한 설명 자체는 길지만 실제 TDD 방식으로 잘 진행된다면 테스트와 기능 구현, 리팩토링까지 빠르게 진행이 가능합니다.
TDD의 장점과 단점
✔ TDD의 장점
TDD의 장점을 정리하자면 다음과 같습니다.
- 테스트를 통과할 만큼의 기능을 구현하므로 한 번에 너무 많은 기능을 구현할 필요가 없습니다.
- 테스트의 코드가 추가되면서 검증하는 범위가 넓어질수록 기능 구현도 점진적으로 완성되어 갑니다.
- 즉, 단순한 기능에서 복잡한 기능으로 확장되면서 그때그때 검증을 빼먹지 않고 할 수 있습니다.
- 리팩토링 할 부분이 눈에 보이면 그때그때 리팩토링을 빠르게 진행하기 때문에 리팩토링의 비용이 상대적으로 적어집니다.
- 우리가 실무에서 하나의 기능을 완성해놓고 보면 리팩토링이 필요한 부분이 눈에 들어오는 경우가 많습니다. 그런데 일정 상의 이유로 리팩토링을 하지 않고, 대충 넘어가는 경우가 상당히 많은데, TDD에 익숙해지면 그때그때 리팩토링을 진행하게 되므로 리팩토링에 대한 비용이 상대적으로 적어질 가능성이 높습니다.
- 이미 잘 동작하는 코드를 수정하는 일은 부담스러운 게 사실입니다. 왜냐고요? ‘수정을 했는데 잘 동작하지 않으면 어떻게하지?’라는 심리적인 불안감이 발생할 수 있기 때문이니까요.
- 그런데 TDD 방식에서는 항상 테스트 케이스가 존재하기 때문에 기존 코드를 수정하더라도 상대적으로 심리적 불안감이 줄어들 수 있습니다.
- 리팩토링을 통해 꾸준히 코드를 개선하므로 코드의 품질을 일정 부분 유지할 수 있습니다.
- 리팩토링이 전혀 이루어지지 않은 상태에서 코드 품질이 점점 나빠져가는 상태로 애플리케이션이 구현되고 있는 모습을 상상해 보세요. 그만큼의 유지 보수 비용이 높아진다는 사실을 알 수 있을 겁니다.
- 코드 수정 이후, 바로 테스트를 진행할 수 있으므로 코드 수정 결과를 빠르게 피드백받을 수 있습니다.
- 수정 결과를 그때그때 확인할 수 있으므로 잘못된 코드가 남아있을 가능성이 상대적으로 줄어듭니다.
✔ TDD의 단점
그렇다면 TDD의 단점은 무엇일까요?
- 가장 큰 단점은 TDD의 개발 방식이 익숙하지 않다는 것입니다.
- 우리가 일반적으로 진행했던 익숙한 개발 방식은 단번에 버리고 TDD 방식으로 개발을 진행한다는 건 상당히 어려운 일입니다.
- 테스트 코드의 작성에 익숙하지 않은 사람, 테스트 코드를 작성하길 원치 않는 사람들에게는 부정적인 방식일 수 있습니다.
- 개발할 시간도 부족한데 테스트 코드를 언제 작성하냐는 분들이 많습니다.
- 어느 정도 일리 있는 말이지만 기능 구현이 끝나더라도 테스트 없이 하나의 애플리케이션이 릴리스 되지 않습니다.
- 개발자 선에서 자체 테스트가 이루어지지 않는다면 QA로 테스트 주도권이 넘어가는 순간 아마 야근의 연속이 될 가능성이 높을 것입니다.
- 따라서 어떤 식으로든 개발자가 테스트를 할 수밖에 없고, 기능 구현이 다 끝난 상태에서 테스트를 수동으로 하게 되면 전체적으로 개발 완료 시간이 더 늘어날 가능성이 높습니다.
- 결국 테스트를 미리미리 하는 게 좋으며, 테스트 시간을 줄이기 위해서는 테스트를 자동화할 수밖에 없습니다. 테스트를 자동화하려면 결국 테스트 코드를 만들 수밖에 없습니다.
- 팀 단위로 개발을 진행해야 하므로 팀원들 간 사전에 협의가 되어야 합니다.
- 개발을 혼자서 할 수는 없습니다.
- TDD 방식을 적용하기 위해서는 팀 차원에서 TDD 방식에 대한 논의가 필요하고, 팀원들 각자가 TDD를 수용하고, 적용해 보려는 합의가 필요합니다.
기존의 개발 방식을 버리고 한 번에 TDD 개발 방식으로 바꾸기는 상당히 어렵습니다.
따라서 작은 기능 구현에 TDD를 점진적으로 도입해 보면서 TDD로 개발하는 방식이 익숙해질 때까지 연습이 필요하며, 이렇게 TDD로 개발을 진행했을 때의 장점에 대한 확신이 든다면 팀이나 부서 차원에서 납득할 만한 이유를 통해 알리는 시간이 필요할 것입니다.