Junit5에서는 @ParameterizedTest
를 통해서 매개변수화 된 테스트를 더 직관적으로 작성하고 실행이 가능해졌다. 가장 큰 의미는 한 테스트에서 각기 다른 파라미터를 이용하여 여러 번의 테스트 수행이 가능하다는 점이다.
Value, Null, Enum, Method 등 다양한 파라미터 테스트를 글을 통해서 확인해보자
public class Numbers {
public static boolean isOdd(int number) {
return number % 2 != 0;
}
}
다음과 같은 함수를 테스트하기 위해서 테스트 코드를 작성해보자. 기존에는 여러 숫자를 직접 넣어서 확인이 필요했다.
@Test
void isOdd_ShouldReturnTrueForOddNumbers() {
Assertions.assertTrue(isOdd(1));
Assertions.assertTrue(isOdd(5));
Assertions.assertTrue(isOdd(3));
Assertions.assertTrue(isOdd(-1));
}
이러한 방식은 테스트 메서드 코드의 길이를 길게 만든다. 물론 Array나 Collections를 이용해서 코드의 길이를 줄일 수 있다. 그러나 다음과 같이 어노테이션을 이용하면 쉽게 여러 인수를 테스트 할 수 있다.
@ParameterizedTest
@ValueSource(ints = {1, 3, 5, -3, 15, Integer.MAX_VALUE}) // six numbers
void isOdd_ShouldReturnTrueForOddNumbers(int number) {
assertTrue(Numbers.isOdd(number));
}
해당 어노테이션을 통해서 지정된 배열을 파라미터 값으로 넘겨줄 수 있다. literal value를 가진 배열만을 넘겨줄 수 있다.
NullSource는 인수에 null 값을 주입할 때 사용할 수 있다. 따라서 java의 primitive type에는 사용이 불가능하다.
@ParameterizedTest
@NullSource
void testWithNullSource(Integer integer) {
Assertions.assertTrue(integer == null);
}
EmptySource은 인수에 빈 값을 전달할 수 있다.
@ParameterizedTest
@EmptySource
void isBlank_ShouldReturnTrueForEmptyStrings(String input) {
assertTrue(Strings.isBlank(input));
}
NullAndEmptySource는 null과 empty 모두를 전달하기 위해 사용할 수 있다.
@ParameterizedTest
@NullAndEmptySource
void isBlank_ShouldReturnTrueForNullAndEmptyStrings(String input) {
assertTrue(Strings.isBlank(input));
}
다른 어노테이션들과 조합하여 추가로 인수에 값을 넘겨줄 수 도 있다.
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = {" ", "\t", "\n"})
void isBlank_ShouldReturnTrueForAllTypesOfBlankStrings(String input) {
assertTrue(Strings.isBlank(input));
}
테스트의 결과는 다음과 같이 나올 것이다.
Enum 클래스가 가진 상수들을 테스트 인수에 주입하여 사용할 수 있다.
@EnumSource
가 가진 속성은 value, name, mode가 있다.
@ParameterizedTest
@EnumSource(Month.class) // passing all 12 months
void getValueForAMonth_IsAlwaysBetweenOneAndTwelve(Month month) {
int monthNumber = month.getValue();
assertTrue(monthNumber >= 1 && monthNumber <= 12);
}
다음과 같이 Enum 클래스를 가져와서 테스트가 가능하다.
@ParameterizedTest
@EnumSource(value = Month.class, names = ".+BER", mode = EnumSource.Mode.MATCH_ANY)
void fourMonths_AreEndingWithBer(Month month) {
EnumSet<Month> months =
EnumSet.of(Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER, Month.DECEMBER);
assertTrue(months.contains(month));
}
mode에는 5가지 정도의 옵션을 선택할 수 있는데 위와 같이 사용이 가능하다. 아래는 5가지 옵션에 대한 목록이고, 사용법은 메서드 이름에 나와있는 것과 같다.
csv 파일에 저장된 텍스트들을 인자에 주입하여 사용할 수 도 있다. 대용량 데이터를 테스트하는 데 적합한 방법이 될 것 같다. ( I/O를 구현할 필요가 없어지는 장점 )
input,expected
test,TEST
tEst,TEST
Java,JAVA
대신 CSV파일은 일정 형식을 갖추고 있어야 하고, @CsvFileSource
어노테이션에서도 그 형식에 맞게 호출을 해줘야 한다. 위의 파일의 경우는 1번 row가 필요가 없기 때문에 1줄은 스킵하고 각 인자에 주입해준다.
@ParameterizedTest
@CsvFileSource(resources = "/data.csv", numLinesToSkip = 1)
void toUpperCase_ShouldGenerateTheExpectedUppercaseValueCSVFile(
String input, String expected) {
String actualValue = input.toUpperCase();
assertEquals(expected, actualValue);
}
보면 알겠지만 이번엔 인자가 두 개이다. 지금까지는 인자가 한 개씩 있었지만 두 개 이상의 인자에도 주입이 가능하다.
이전에 CSV파일을 통해 두 가지 인수를 받았다. 그 이전에는 ValueSource와 Enum을 이용해서 한 가지 인수를 받았다. 두 가지 이상의 인수를 파일이 아닌 형식으로 좀 더 자유롭게 받고자 한다면 여기를 잘 봐야 한다.
private static List<Arguments> provideStringsForIsBlank() {
return List.of(
Arguments.of(null, true),
Arguments.of("", true),
Arguments.of(" ", true),
Arguments.of("not blank", false)
);
}
다음과 같이 인수를 2개 이상 반환 가능한 Arguments.of
를 이용해서 Collection에 담아서 static 메서드를 통해 반환하는 함수를 만든다.
@ParameterizedTest
@MethodSource("provideStringsForIsBlank")
void isBlank_ShouldReturnTrueForNullOrBlankStrings(String input, boolean expected) {
assertEquals(expected, Strings.isBlank(input));
}
@MethodSource
를 통해서 함수에서 인수를 받아온다. 필요하다면 3개 그 이상의 인수를 받아올 수 있을 것이다. MethodSource의 값을 조정하여 같은 파일이 아닌 외부 패키지의 경로를 입력하여 메서드에서 받아오는 것도 충분히 가능하다.
Custom Argument Provider, Custom Annotation 등 다양한 방법으로 인수를 주입하여 Parameterized Test가 가능하다. 상황에 맞는 방법을 선택하여 테스트 코드 작성을 고려하면 될 것 같다. 무엇보다 중요한 점은 한 싱글 테스트에서 여러 번의 테스트 수행을 각기 다른 파라미터로 가능하다는 점이다.