[TDD] Parameterized Tests

DevHwan·2023년 9월 1일
0

들어가며

Junit5에서는 @ParameterizedTest 를 통해서 매개변수화 된 테스트를 더 직관적으로 작성하고 실행이 가능해졌다. 가장 큰 의미는 한 테스트에서 각기 다른 파라미터를 이용하여 여러 번의 테스트 수행이 가능하다는 점이다.

Value, Null, Enum, Method 등 다양한 파라미터 테스트를 글을 통해서 확인해보자

ValueSource

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, EmptySource, NullAndEmptySource

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

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

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);
}

보면 알겠지만 이번엔 인자가 두 개이다. 지금까지는 인자가 한 개씩 있었지만 두 개 이상의 인자에도 주입이 가능하다.

Method

이전에 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가 가능하다. 상황에 맞는 방법을 선택하여 테스트 코드 작성을 고려하면 될 것 같다. 무엇보다 중요한 점은 한 싱글 테스트에서 여러 번의 테스트 수행을 각기 다른 파라미터로 가능하다는 점이다.

profile
달리기 시작한 치타

0개의 댓글