TestCode 작성하는 이유
Why Write TestCode
- 코드에대한 문서화
- 코드에 대한 결함을 발견하기 위함
- 리팩토링 시 안정성 확보
- Documentation for code
- To discover defects in code
- To ensure stability during refactoring
TDD란?
What is TDD?
- Test Driven Development
- 프로덕션 코드보다 테스트코드를 먼저 작성하는 개발 방법
- TFD(Test First Development) + Refactoring
- 기능 동작을 먼저 검증 (메소드 단위)
- Test Driven Development
- A development method where test code is written before production code
- TFD(Test First Development) + Refactoring
- Verify functionality first (at the method level)
BDD도 있던데?
What about BDD?
- Behavior Driven Development
- 시나리오 기반으로 테스트코드를 작성하는 방법
- 하나의 시나리오는 Given / When / Then 구조
- Behavior Driven Development
- A method of writing test code based on scenarios
- Each scenario follows the Given / When / Then structure
비밀번호 유효성 검증기
Password Validator
요구사항 및 기능명세
Requirements and Specifications
- 비밀번호는 최소 9자 이상 15자 이하
- 비밀번호가 9자 미만 또는 15자 초과인경우 Exception ㅂ라생
- 경계조건 확인
- Password must be at least 9 characters and no more than 15 characters
- Throw an Exception if the password is less than 9 characters or more than 15 characters
- Check boundary conditions
Dependency
plugins {
id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
testImplementation 'org.assertj:assertj-core:3.23.1'
}
test {
useJUnitPlatform()
}
비밀번호 길이 검증 TestCode 작성
Writing TestCode for Password Length Validation
테스트 클래스 및 기본 클래스 생성하기
Creating Test Class and Basic Class
- 테스트코드의 패키지의 위치와, main 소스의 패키지의 위치가 동일하게 생성되면 좋다
- 테스트코드에서 요구조건을 먼저 실현하고, 실현한 요구조건에서 실제 코드에 반영되지 않은 부분들을 하나씩 만들어가며 작업
- It's good to create the test code package in the same location as the main source package
- First implement the requirements in the test code, then gradually build out the parts not yet reflected in the actual code
package org.example;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThatCode;
/**
* 비밀번호는 최소 9자 이상 15자 이하
* 비밀번호가 9자 미만 또는 15자 초과인경우 Exception ㅂ라생
* 경계조건 확인
*/
public class PasswordValidatorTest {
@DisplayName("비밀번호가 최소 9자 이상 15자 이하면 정상") // 테스트 의도
@Test
void validatePasswordTest() {
assertThatCode(() -> PasswordValidator.validate("123456789"))
.doesNotThrowAnyException();
}
}
- 작성 한 후, PasswordValidator 클래스에 붉은줄이 생성됨
- PasswordValidator를 main source에 생성
- PasswordValidator 클래스에 validate 메소드를 생성
- After writing, a red underline appears on the PasswordValidator class
- Create PasswordValidator in the main source
- Create the validate method in the PasswordValidator class
package org.example;
public class PasswordValidator {
public static void validate(String password) {
}
}
- 테스트 진행 -> 성공
- Run test -> Success
생성 된 클래스들을 Refactoring 하여 상세조건 반영하기
Refactoring Created Classes to Reflect Detailed Conditions
- 입력된 문자열이 9자 이상 15자 이하인지 확인하고, 아니라면 Exception 발생 코드 작성
- Write code to check if the input string is between 9 and 15 characters, and throw an Exception if not
package org.example;
public class PasswordValidator {
public static final String WRONG_PASSWORD_LENGTH_EXCEPTION_MESSAGE = "비밀번호는 최소 9자 이상 15자 이하입니다.";
public static void validate(String password) {
int length = password.length();
if (length < 9 || length > 15) {
throw new IllegalArgumentException(WRONG_PASSWORD_LENGTH_EXCEPTION_MESSAGE);
}
}
}
- 테스트실행 -> 성공
- Run test -> Success
비밀번호가 9자 미만인 경우 테스트
Testing When Password is Less Than 9 Characters
- 위와 같은방법으로, TestCode에서 먼저 시도하고 실제 코드에 하나씩 반영하는 방법
- 이미 Exception 관련 내용을 작성했기 때문에 크게 수정 할 내용이 없다 조건만 테스트
- Using the same method, first try in TestCode and then reflect in the actual code one by one
- Since the Exception-related content has already been written, there's not much to modify - just test the condition
public class PasswordValidatorTest {
@DisplayName("비밀번호가 9자 미만인 경우 Exception 발생")
@Test
void validatePasswordShortExceptionTest() {
assertThatCode(() -> PasswordValidator.validate("1234"))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining(PasswordValidator.WRONG_PASSWORD_LENGTH_EXCEPTION_MESSAGE);
}
}
비밀번호가 15자 초과인 경우 테스트
Testing When Password Exceeds 15 Characters
- 위와 같은방법으로, TestCode에서 먼저 시도하고 실제 코드에 하나씩 반영하는 방법
- 이미 Exception 관련 내용을 작성했기 때문에 크게 수정 할 내용이 없다 조건만 테스트
- Using the same method, first try in TestCode and then reflect in the actual code one by one
- Since the Exception-related content has already been written, there's not much to modify - just test the condition
public class PasswordValidatorTest {
@DisplayName("비밀번호가 15자 초과인 경우 Exception 발생")
@Test
void validatePasswordLongExceptionTest() {
assertThatCode(() -> PasswordValidator.validate("1234512345123456"))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining(PasswordValidator.WRONG_PASSWORD_LENGTH_EXCEPTION_MESSAGE);
}
}
경계조건에 대한 테스트
Testing Boundary Conditions
- 우리의 요구조건에 부합하는 비밀번호의 길이는 9자 이상 15자 이하이다.
- 위의 조건에대한 경계값은 비밀번호가 8자, 혹은 16자 일 경우이다
- 경계값에 대한 테스트를 추가 해 주면 좋은 테스트를 작성할 수 있다.
@Parameterize를 추가하여 테스트 해 보자
- The password length that meets our requirements is at least 9 characters and no more than 15 characters.
- The boundary values for the above condition are when the password is 8 characters or 16 characters
- Adding tests for boundary values helps write good tests.
- Let's test using
@Parameterize
Dependency 추가
Adding Dependency
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.0'
Parameterize를 이용한 테스트
Testing with Parameterize
public class PasswordValidatorTest {
@DisplayName("경계조건에 대해 테스트")
@ParameterizedTest
@ValueSource(strings = {"12345678", "1234567890123456"})
void validatePasswordBoundaryTest(String password) {
assertThatCode(() -> PasswordValidator.validate(password))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining(PasswordValidator.WRONG_PASSWORD_LENGTH_EXCEPTION_MESSAGE);
}
}
- ValueSource 내에 들어가있는 값들을 자동으로 바꿔가며 실행시켜 준다
- ValueSource 내에 선언된 데이터를 함수에서 하나씩 받아 줄 수 있다
- 실행을 해 보면, 다른 테스트와는 다르게 테스트가 두번 돌아간다
- The values inside ValueSource are automatically substituted and executed
- The data declared in ValueSource can be received one by one in the function
- When you run it, unlike other tests, the test runs twice
결과화면
Result Screen
