REF : https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests
Parameterized Test와 관련된 공식 document를 번역+요약+개인 의견을 첨부하며 실제 타이핑해본 것을 정리한 포스트입니다...!!
Parameterized Test란?
- 여러 argument를 이용해 테스트를 여러번 돌릴 수 있는 테스트를 할 수 있는 기능
- 사용하기 위해서는
@Test
대신@ParameterizedTest
를 붙이면 된다. @ParameterizedTest
를 사용하게 되면 최소 하나의 source 어노테이션을 붙여주어야 한다. 예를 들어, 다음 테스트는 배열로 argument를 전달하는@ValueSorce
이다
@ParameterizedTest
@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })
void palindromes(String candidate) {
// Palindrome은 회문이다 ㅎㅎ 토마토 기러기 처럼..!!
assertTrue(StringUtils.isPalindrome(candidate));
}
이 테스트를 수행하면 결과가 다음과 같이 나오게 된다. (총 3번 테스트가 돌아가는 것이다)
palindromes(String) ✔
├─ [1] candidate=racecar ✔
├─ [2] candidate=radar ✔
└─ [3] candidate=able was I ere I saw elba ✔
다양한 Source 종류들
Parameterized Test 사용법은 생각보다 간단하다. 이제 중요한 것은 파라미터에 들어갈 source를 어떻게 넣어줄 것인가로 생각된다. 이제 사용할 수 있는 source annotation들에 대해 살펴보자
ValueSource
- argument가 하나인 테스트에 사용할 수 있다.
- short, byte, int, long ,float, double, char, boolean, String, java.lang.Class에 사용할 수 있다.
사용자가 직접 만든 클래스는 사용할 수 없는 듯 하다
@ParameterizedTest
@ValueSource({new Car(1000, "현대차")}) // 여기서 에러가 난다
void valueSourceTest(car car) {
}
NullSource, EmptySource, NullAndEmptySource
- NullSource : null을 보낸다
- EmptySource : 비어 있는 배열, 컬렉션, 문자열 등을 반환한다
- NullAndEmptySource : NullSource와 EmtpySource를 합쳐놓은 것이다
@ParameterizedTest
@NullAndEmptySource
void nullAndEmptySourceTest(List<String> list) {
System.out.println(list); // null과 빈 리스트([])가 각각 출력된다
}
EnumSource
- Enum을 argument로 보내줄 수 있다. (역시 argument가 하나이다 ㅠㅠ)
다음과 같은 enum이 있다고 하자!!
public enum Planet {
EARTH,
MARS,
JUPITER;
}
이 Enum을 EnumSource로 활용할 수 있는 방법은 3가지 정도 존재한다
- Enum 전체를 가져오기
@ParameterizedTest
@EnumSource
void enumSourceTest(Planet planet) {
// EARTH, MARS, JUPITER가 각각 들어오게 된다
// Planet이라는 enum 구현체를 파라미터 타입에 명시했기 때문이다
// @EnumSource(Planet.class) 라고 할 수도 있다
}
- Enum의 특정 목록만 가져오기
@ParameterizedTest
@EnumSource(names = {"MARS", "JUPITER"})
void enumSourceTest(Planet planet) {
// MARS, JUPITER만 각각 들어오게 된다
}
@ParameterizedTest
@EnumSource(mode = EXCLUDE, names = {"MARS", "JUPITER"})
void enumSourceTest(Planet planet) {
// EARTH만 들어온다
}
- regex 사용하기
@ParameterizedTest
@EnumSource(mode = MATCH_ALL, names = "^.*RS$")
void enumSourceTest(Planet planet) {
// MARS만 들어온다
}
MethodSource
- 테스트 클래스 내의 메소드 혹은 외부 클래스의 메소드가 반환하는 값을 source로 삼는 것이다.
- 테스트 클래스 내에 있고
@TestInstance(Lifecycle.PRE_CLASS)
를 붙인 것이 아니라면, 모두 static 메소드여야 한다. - 한 파라미터만 넣을 수도 있고, 여러 파라미터를 넣을 수도 있다.
- 테스트 메소드와 이름이 동일하면 source를 명시적으로 적어주지 않아도 된다.
- 파라미터를 한 개만 넣으려면
Stream
을 반환하면 된다.IntStream
,DoubleStream
,LongStream
등을 반환해도 괜찮다.
@ParameterizedTest
@MethodSource("parameterProvider")
void methodSourceTest(String argument) {
// apple, banana가 각각 들어온다
}
static Stream<String> parameterProvider() {
return Stream.of("apple", "banana");
}
- 파라미터를 여러개 넘기려면
Stream<Arguments>
를 반환해야 한다.Arguments
객체는Arguments.of
혹은arguments
로 생성할 수 있다.
@ParameterizedTest
@MethodSource("parametersProvider")
void methodSourceTest(String str, int num, List<String> list) {
// (apple, 1, [a, b])랑 (banana, 2, [x, y])
}
static Strema<Arguments> parametersProvider() {
return Stream.of(
arguments("apple", 1, Arrays.asList("a", "b")),
arguments("banana", 2, Arrays.asList("x", "y"))
);
}
CsvSource
- CsvSource는 우리가 흔히 알고 있는
, 로 분리된 문자열
을 테스트 메소드의 파라미터로 넣어준다 - delimiterString 이라는 옵션으로 문자열을 분리할 다른 구분자를 고를 수 있다.
' '
로 넣는 것은 empty가 들어오고 아예 비워두는 것은 null이 들어오게 된다
@ParameterizedTest
@CsvSource({
"apple, 1",
"banana, 2",
"'lemon, lime', 3"
})
void csvSourceTest(String fruti, int rank) {
// (apple, 1) (banana, 2) ("lemon, lime", 3)이 각각 들어온다
}
이 외에도 CsvFileSource, ArgumentSource 등이 있으나 잘 사용될 것 같지는 않다.
자동 형변환
Argument로 문자열이 들어올때 특별한 형식에 맞추면 기본적으로 다른 Type의 객체로 바꿔준다..!! 예를 들어 "2020-01-01"이 들어오면 LocalDate로 들어올 수 있다.
자세한 형변환은 여기를 확인해보면 된다.
그리고 만약, 형변환 규칙이 적용되지 않았을 때는 public 정적 팩토리 메소드나 public 생성자에 주어진 파라미터 들을 넣어 보는 fallback 시나리오가 존재한다.
@ParameterizedTest
@ValueSource(strings = "42 Cats")
void testWithImplicitFallbackArgumentConversion(Book book) {
assertEquals("42 Cats", book.getTitle());
}
public class Book {
private final String title;
private Book(String title) {
this.title = title;
}
public static Book fromTitle(String title) {
return new Book(title);
}
public String getTitle() {
return this.title;
}
}
예를 들어 위의 경우에는 "42 Cats" 문자열이 Book으로 바뀌는 기본 정책은 링크가 걸려 있는 목록에서 확인할 수 없지만, Book 객체의 public 정적 팩토리인 fromTitle 메소드가 자동 호출되어 Book 객체로 변환될 수 있다.
명시적으로 SimpleArgumentConverter를 상속한 ArgumentConverter를 만들 수도 있다. (많이 쓰일 것 같지는 않다)
Display Name 조절하기
@DisplayName("Display name of container")
@ParameterizedTest(name = "{index} ==> the rank of ''{0}'' is {1}")
@CsvSource({ "apple, 1", "banana, 2", "'lemon, lime', 3" })
void testWithCustomDisplayNames(String fruit, int rank) {
}
@ParameterizedTest
에 name 옵션을 주어, index, {0}, {1} 등등을 사용할 수 있다. index는 1부터 시작하는 반복횟수이고 {0}과 {1}은 첫 번째 argument, 두 번째 argument이다. 예를 들어 위의 테스트는 아래와 같이 출력된다.
Display name of container ✔
├─ 1 ==> the rank of 'apple' is 1 ✔
├─ 2 ==> the rank of 'banana' is 2 ✔
└─ 3 ==> the rank of 'lemon, lime' is 3 ✔
추가로 쓸 수 있는 템플릿은 아래와 같다.
- {displanName} : method의 이름을 보여준다
- {arguments} : comma로 구분된 파라미터를 모두 보여준다
- {argumentsWithNames} : comma로 구분된 파라미터 이름과 파라미터를 모두 보여준다
'개발 공부 기록하기 > 01. JAVA & Kotlin' 카테고리의 다른 글
짧은 코멘트와 함께하는 이펙티브 자바) #3 private 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2020.09.18 |
---|---|
짧은 코멘트와 함께하는 이펙티브 자바) #2 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2020.09.17 |
짧은 코멘트와 함께하는 이펙티브 자바) #1 생성자 대신 정적 팩토리 메소드를 고려하라 (0) | 2020.09.16 |
Java OutputStream이란? (4) | 2020.09.01 |
Java InputStream이란? (7) | 2020.08.21 |