<1장> TDD 개발 준비
TDD
- Test-Driven Development
- 테스트 주도 개발
IDE 내에서 테스트 실행
public class StringTest {
@Test
void substring() {
String str = "abcde";
assertEquals("cd", str.substring(2, 4));
}
}
터미널에서 테스트 실행
# mvn command
$ mvn test # maven test execution
$ mvnw # maven wrapper
# gradle command
$ gradle test # gradle test execution
$ gradlew # gradle wrapper
<2장> TDD 시작
TDD
- 테스트 ➡️ 구현
- 테스트를 먼저 한다 = 기능이 올바르게 작동하는지 검증하는 테스트 코드 작성
Assertions.assertEquals()
- 두 값이 같은지 비교하는 단언(assertion)
- 단언 : 값이 특정 조건을 충족하는지 확인 후, 충족하지 않으면 예외 발생
e.g. 1
/src/test/java/
- 해당 경로는 배포 대상이 아니기에 미완성 코드가 배포되는 것을 방지하는 효과 있음
public class CalculatorTest {
@Test
void plus() {
int result = Calculator.plus(1, 2);
assertEquals(3, result);
}
}
/src/main/java/
public class Calculator {
// 현재 덧셈 기능 위해 새 객체를 만들 필요가 없기에
// 정적 메서드로 구현(인스턴스 메서드 x)
public static int plus(int a1, int a2) {
return a1 + a2;
}
}
e.g. 2
예시의 검사 규칙
- 길이 8글자 이상 / 0 ~ 9 사이의 숫자 포함 / 대문자 포함
- 세 규칙 모두 충족 시 암호 : 강함
- 2개 규칙 충족 시 암호 : 보통
- 1개 이하 규칙 충족 시 암호 : 약함
/src/test/java/
// 테스트 코드도 코드이기에 유지보수 대상
public class PasswordStrengthMeterTest {
// 공통 부분 추출
private PasswordStrengthMeter meter = new PasswordStrengthMeter();
private void assertStrength(String password, PasswordStrength expStr) {
PasswordStrength result = meter.meter(password);
assertEquals(expStr, result);
}
// 각 조건 별 테스트
@Test
void meetsAllCriteria_Then_Strong() {
assertStrength("ab12!@AB", PasswordStrength.STRONG);
assertStrength("abc1!Add", PasswordStrength.STRONG);
}
@Test
void meetsOtherCriteria_except_for_Length_Then_Normal() {
assertStrength("ab12!@A", PasswordStrength.NORMAL);
}
@Test
void meetsOtherCriteria_except_for_number_Then_Normal() {
assertStrength("ab!@ABqwer", PasswordStrength.NORMAL);
}
@Test
void nullInput_Then_Invalid() {
assertStrength(null, PasswordStrength.INVALID);
}
@Test
void meetsOtherCriteria_except_for_Uppercase_Then_Normal() {
assertStrength("ab12!@df", PasswordStrength.NORMAL);
}
@Test
void meetsOnlyLengthCriteria_Then_Weak() {
assertStrength("abdefghi", PasswordStrength.WEAK);
}
@Test
void meetsOnlyNumCriteria_Then_Weak() {
assertStrength("12345", PasswordStrength.WEAK);
}
@Test
void meetsOnlyUpperCriteria_Then_Weak() {
assertStrength("ABZEF", PasswordStrength.WEAK);
}
@Test
void meetsNoCriteria_Then_Weak() {
assertStrength("abc", PasswordStrength.WEAK);
}
}
/src/main/java/
public enum PasswordStrength {
INVALID, WEAK, NORMAL, STRONG
}
public class PasswordStrengthMeter {
public PasswordStrength meter(String s) {
if(s == null || s.isEmpty()) { // if(StringUtils.isBlank(s))
return PasswordStrength.INVALID;
}
// 개별 규칙을 검사하는 로직
int metCounts = getMetCriteriaCounts(s);
// 규칙을 검사한 결과에 따라 암호 강도를 계산하는 로직
if(metCounts <= 1) {
return PasswordStrength.WEAK;
}
if(metCounts == 2) {
return PasswordStrength.NORMAL;
}
return PasswordStrength.STRONG;
}
private int getMetCriteriaCounts(String s) {
int metCounts = 0;
if(s.length() >= 8) {
metCounts++;
}
if(meetsContainingNumberCriteria(s)) {
metCounts++;
}
if(meetsContainingUpperCriteria(s)) {
metCounts++;
}
return metCounts;
}
private boolean meetsContainingNumberCriteria(String s) {
for(char ch : s.toCharArray()) {
if('0' <= ch && ch <= '9') {
return true;
}
}
return false;
}
private boolean meetsContainingUpperCriteria(String s) {
for(char ch : s.toCharArray()) {
if(Character.isUpperCase(ch)) {
return true;
}
}
return false;
}
}
TDD 흐름
TDD 사이클
- 레드 - 그린 - 리팩터
- 레드 = 실패하는 테스트
- 그린 = 성공한 테스트 = 코드 구현해 실패 테스트를 통과시킴
- 리팩터 = 리팩토링
TDD 장점
- 테스트가 개발을 주도
- 지속적인 코드 정리
- 빠른 피드백
'Engineering' 카테고리의 다른 글
[테스트 주도 개발 시작하기] 11장 ~ 부록 (0) | 2023.06.22 |
---|---|
[테스트 주도 개발 시작하기] 9 ~ 10장 (0) | 2023.06.22 |
[테스트 주도 개발 시작하기] 7 ~ 8장 (0) | 2023.06.15 |
[테스트 주도 개발 시작하기] 5 ~ 6장 (0) | 2023.06.14 |
[테스트 주도 개발 시작하기] 3 ~ 4장 (0) | 2023.06.13 |
댓글