솔솔
[Junit] 반복되는 테스트 코드 줄이기: @ParameterizedTest로 여러 케이스 한 번에 실행하기 본문
🍀 들어가기에 앞서
테스트 공부를 하다가
테스트 케이스를 세분화를 하면서 궁금증이 생겼다. (어디까지 세분화 해야되는 것인가?)
아래 처럼 이러한 기능의 함수가 있다면
public void add(Beverage beverage, int count) {
if (count < 0) {
throw new IllegalArgumentException("음료는 1잔 이상 주문하실 수 있습니다.");
}
for (int i = 0; i < count; i++) {
beverages.add(beverage);
}
}
모든 경계값(0, -1..)에 대해서도 테스트 케이스를 세분화해서 만들어야 되는건지
// 경계값 0에 대한 테스트 함수
@Test
void addZeroBeverages() {
... (생략)
}
// 경계값 -1에 대한 테스트 함수
@Test
void addNegativeOneBeverages() {
... (생략)
}
아니면 테스트 케이스 하나를 만들어서 그안에 값을 변경하면서 테스트해야되는건지 궁금했다.
@Test
void addZeroAndNegativeOneBeverages() {
... (생략)
// 0값 체크 후 테스트 통과하면 1로 고쳐서 테스트
assertThatThrownBy(() -> cafeKiosk.add(americano, 0))
.isInstanceOf(IllegalAccessError.class)
.hasMessage("음료는 1잔 이상 주문하실 수 있습니다.");
}
결론은 값을 매번 수동으로 바꿔가면서 테스트 하는것은 적절하지 않기 때문에
경계값이라고 생각하는 값에 대해 테스트 케이스를 세분화해서 작성해야 한다.
🍀 반복되는 테스트 코드 줄이기
앞에 이야기에 이어서
경계값이라고 생각하는 값에 대해 테스트 케이스를 세분화해서 작성하는건 좋은데
경계값이 2~3개 였다가
어떠한 이유로 규칙이 늘어나서 경계값이 굉장히 많이 생기게되면
그만큼 늘어날 때 마다 새로운 테스트 메서드를 추가해야 한다.
동일한 로직을 검증하는 과정에서 여러 개의 테스트 메서드를 작성해야 한다면
어떻게 보면 코드가 불필요하게 반복되는 문제가 있다.
(만약 7개의 테스트 메서드가 있다면 스크롤을 내리면서
"오우...어디까지 테스트 한거지 굉장히 많군, 억 어디까지 봤지?"이라고 생각이 들것 같다.)
만약 어떠한 로직을 검증한는 과정에서 같은 로직임에도 불구하고
이렇게 여러 값에 의해 여러 테스트 함수를 작성해야 될 경우
Junit에서는 @ParameterizedTest라는 어노테이션을 제공한다.
🍀 @ParameterizeTest 활용
@ParameterizedTest 어노테이션을 사용하면서 파라미터로 넘겨줄 값들을 지정해야되는데
JUnit5에서는 다양한 소스(Source)에서 데이터를 제공할 수 있도록 여러 가지 방법을 지원한다.
- @ValueSource: 단순한 기본 데이터 타입 배열을 제공
- @CsvSource: 여러 개의 CSV 형태의 값을 제공
- @MethodSource: 메서드에서 직접 테스트 데이터를 제공
- @EnumSource: Enum 값을 활용한 테스트
- @NullSource, @EmptySource, @NullOrEmptySource: null또는 공백값에 대한 테스트
앞선 코드를 @ParameterizeTest 어노테이션을 활용하면
아래 처럼 작성이 가능하다.
@ParameterizedTest
@DisplayName("카페음료 주문 시 수량 -1, 0 입력 시 예외처리가 된다.")
@ValueSource(ints = {-1, 0})
void addBeverages(int quantity) {
CafeKiosk cafeKiosk = new CafeKiosk();
Americano americano = new Americano();
// 예외처리 확인
assertThatThrownBy(() -> cafeKiosk.add(americano, quantity))
.isInstanceOf(IllegalArgumentException.class) // 어떤 예외가 터지는가?
.hasMessage("음료는 1잔 이상 주문하실 수 있습니다."); // 어떤 예외 메시지를 가지고 있는가?
}
해당 테스트 메소드를 돌려본 결과
@ValueSource로 통해 전달한 두 인수 값에 대한 두 개의 테스트가 진행되었고 테스트 전부 성공했다.
여튼
이러한 기능을 활용하면 테스트 코드의 가독성과 유지보수성을 높일 수 있으며, 경계값이 늘어나더라도 쉽게 확장할 수 있다.
또한, 테스트 과정에서 예상치 못한 오류를 발견하며 테스트 코드의 중요성을 다시 한번 깨닫게 되었다라는 이야기.
✅ 참고한 사이트 (더 유용한 어노테이션 정보들이 많음)
https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests
JUnit 5 User Guide
Although the JUnit Jupiter programming model and extension model do not support JUnit 4 features such as Rules and Runners natively, it is not expected that source code maintainers will need to update all of their existing tests, test extensions, and custo
junit.org