구현할 기능: 암호 검사기
검사 규칙
- 길이가 8자 이상
- 0 ~9 숫자를 포함
- 대문자 포함
암호 강도
- 3개 규칙 모두 충족 시 암호는 강함
- 2개 규칙 충족 시 암호는 보통
- 1개 규칙 충족 시 암호는 약함
1. 첫 번째 테스트
가장 쉽거나 가장 예외적인 상황을 선택해야 함
모든 규칙을 충족하는 경우, 모든 조건을 충족하지 않은 경우 중 모든 규칙을 충족하는 경우를 먼저 테스트
왜? 모든 규칙을 충족시키지 않는 경우 테스트를 통과시키려면 각 조건을 검사하는 코드를 모두 구현해야 하므로 한 번에 만들어야 할 코드가 많아지고 시간이 길어짐 -> 사실상 구현을 다 하고 테스트 하는 방식과 큰 차별점이 없음
1) 테스트 코드 구현
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class PasswordStrengthMeterTest {
@Test
@DisplayName("비밀번호 강도 강함")
void meetsAllCriteria_Then_Strong() {
PasswordStrengthMeter meter = new PasswordStrengthMeter();
PasswordStrength result = meter.meter("ab12!!@AB"); // 리턴 타입 고민 후 적절한 타입을 사용해야 함
assertEquals(PasswordStrength.STRONG, result);
PasswordStrength result2 = meter.meter("abc12!dDd");
assertEquals(PasswordStrength.STRONG, result2);
}
}
-> PasswordStrengthMeter 타입과 PasswordStrength 타입이 없으므로 컴파일 에러 발생
2) 컴파일 에러를 없애기 위해 PasswordStrengthMeter 클래스 생성
public class PasswordStrengthMeter {
public PasswordStrength meter(String s) {
return PasswordStrength.STRONG;
3) 컴파일 에러를 없애기 위해 PasswordStrength 클래스 생성
public enum PasswordStrength {
STRONG
}
-> 컴파일 에러가 없으므로 테스트 실행 가능, STRONG을 반환 하므로 테스트 통과 성공
2. 두 번째 테스트
길이가 8글자 이상을 충족시키지 못하고, 다른 2개 조건은 충족하는 경우
1) 테스트 코드 추가
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class PasswordStrengthMeterTest {
// 생략
@Test
@DisplayName("비밀번호 강도 보통 - 길이 8자 미만")
void meetsOtherCriteria_except_for_Then_Normal() {
PasswordStrengthMeter meter = new PasswordStrengthMeter();
PasswordStrength result = meter.meter("ab12!@A");
assertEquals(PasswordStrength.NORMAL, result);
PasswordStrength result2 = meter.meter("ac@wS2!");
assertEquals(PasswordStrength.NORMAL, result2);
}
}
-> PasswordStrength 타입에 NORMAL이 없으므로 컴파일 에러 발생
2) PasswordStrength 클래스에 NORMAL 추가
public enum PasswordStrength {
STRONG,
NORMAL
}
-> 추가 후 테스트 코드를 실행하면 테스트 실패
3) 테스트 코드가 모두 통과 될 수 있도록 PasswordStrengthMeter 클래스에 코드 추가
public class PasswordStrengthMeter {
public PasswordStrength meter(String s) {
if(s.length() < 8) {
return PasswordStrength.NORMAL;
}
return PasswordStrength.STRONG;
}
}
-> 코드를 추가하면 테스트 통과 성공
3. 세 번째 테스트
숫자를 포함하는 조건을 충족 시키지 못하고, 다른 2개 조건은 충족하는 경우
1) 숫자를 포함하는 조건을 충족시키지 못하는 테스트 코드 추가
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class PasswordStrengthMeterTest {
// 생략
@Test
@DisplayName("비밀번호 강도 보통 - 숫자 미포함")
void meetsAllOtherCriteria_except_for_number_Then_Normal() {
PasswordStrengthMeter meter = new PasswordStrengthMeter();
PasswordStrength result = meter.meter("abcd!!@A");
assertEquals(PasswordStrength.NORMAL, result);
PasswordStrength result2 = meter.meter("ac@wSfe!e");
assertEquals(PasswordStrength.NORMAL, result2);
}
}
-> 길이는 8자 이상이기 때문에 STRONG이 결과값이기 때문에 테스트 코드 실패
2) 테스트 코드가 모두 통과 될 수 있도록 PasswordStrengthMeter 클래스에 코드 추가
public class PasswordStrengthMeter {
public PasswordStrength meter(String s) {
if(s.length() < 8) {
return PasswordStrength.NORMAL;
}
boolean containsNum = false;
for(char ch : s.toCharArray()) {
if(ch >= '0' && ch <= '9') {
containsNum = true;
break;
}
}
if (!containsNum) return PasswordStrength.NORMAL;
return PasswordStrength.STRONG;
}
}
4. 코드 리팩터링
코드가 다소 길어지고 가독성이 좋지 않으므로 코드를 리팩터링함
1) PasswordStrengthMeter 클래스 코드 리팩터링
public class PasswordStrengthMeter {
public PasswordStrength meter(String s) {
if(s.length() < 8) {
return PasswordStrength.NORMAL;
}
boolean containsNum = meetsContainingNumberCriteria(s);
if (!containsNum) return PasswordStrength.NORMAL;
return PasswordStrength.STRONG;
}
private boolean meetsContainingNumberCriteria(String s) {
for(char ch : s.toCharArray()) {
if(ch >= '0' && ch <= '9') {
return true;
}
}
return false;
}
}
5. 테스트 코드 리팩터링
- 테스트 코드도 코드이기 때문에 유지보수 대상
- 테스트 메소드에서 발생하는 중복을 제거하거나 의미가 드러나도록 코드 수정
- 코드 수정 후, 테스트 재 실행 : 코드 수정으로 테스트가 깨지지 않았는 지 확인
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class PasswordStrengthMeterTest {
private PasswordStrengthMeter meter = new PasswordStrengthMeter(); // 중복되는 코드, 필드에서 생성하도록 수정
private void assertStrength(String password, PasswordStrength expStr) { // 중복되는 코드, 메소드를 이용해서 제거
PasswordStrength result = meter.meter(password);
assertEquals(expStr, result);
}
@Test
@DisplayName("비밀번호 강도 강함")
void meetsAllCriteria_Then_Strong() {
assertStrength("ab12!!@AB", PasswordStrength.STRONG);
assertStrength("abc12!dDd", PasswordStrength.STRONG);
}
@Test
@DisplayName("비밀번호 강도 보통 - 길이 8자 미만")
void meetsOtherCriteria_except_for_Then_Normal() {
assertStrength("ab12!@A", PasswordStrength.NORMAL);
assertStrength("ac@wS2!", PasswordStrength.NORMAL);
}
@Test
@DisplayName("비밀번호 강도 보통 - 숫자 미포함")
void meetsAllOtherCriteria_except_for_number_Then_Normal() {
assertStrength("abcd!!@A", PasswordStrength.NORMAL);
assertStrength("ac@wSfe!e", PasswordStrength.NORMAL);
}
}
'Spring > Test' 카테고리의 다른 글
테스트 주도 개발 시작하기 - TDD 시작, TDD 암호 검사기 3 (0) | 2022.11.27 |
---|---|
테스트 주도 개발 시작하기 - TDD 시작, TDD 암호 검사기 2 (0) | 2022.11.27 |
테스트 주도 개발 시작하기 - TDD 시작, TDD란 (0) | 2022.11.27 |
📝 테스트 코드 작성의 장/단점과 테스트 범위에 따른 분류 (0) | 2022.11.18 |
테스트 코드와 제이유닛(JUnit) (0) | 2022.11.16 |