테스트 코드/JUnit

[JUnit] JUnit5 사용법 - Parameterized Tests

Wings of Freedom 2020. 12. 23. 17:53

Maven Dependencies

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-params</artifactId>
    <version>5.4.2</version>
    <scope>test</scope>
</dependency>
testCompile("org.junit.jupiter:junit-jupiter-params:5.4.2")

Parameterized Tests

  • @ParameterizedTest
    • 이 annotation을 추가하는 것을 제외하고는 다른 테스트와 동일하다.
  • e.g.
      @ParameterizedTest
      @ValueSource(ints = {1, 3, 5, -3, 15, Integer.MAX_VALUE}) // six numbers
      void isOdd_ShouldReturnTrueForOddNumbers(int number) {
          assertTrue(Numbers.isOdd(number));
      }
    
    • 6번의 isOdd 메서드를 실행한다.
    • a source of arguments: an int array
    • a way to access them: the number parameter

1. Simple Value

  • @ValueSource
    • 해당 annotation 에 지정한 배열을 파라미터 값으로 순서대로 넘겨준다.
    • test method 실행 당 하나의 인수(argument) 만을 전달할 때 사용할 수 있다.
    • 리터럴 값의 배열을 테스트 메서드에 전달한다.
    • c.f. literal values 종류
      • short, byte, int, long, float, double, char, java.lang.String, java.lang.Class
  • e.g. 하나의 인수(String input)만을 가짐
      public class Strings {
          public static boolean isBlank(String input) {
              return input == null || input.trim().isEmpty();
          }
      }
    
      @ParameterizedTest
      @ValueSource(strings = {"", "  "})
      void isBlank_ShouldReturnTrueForNullOrBlankStrings(String input) {
          assertTrue(Strings.isBlank(input));
      }
    

2. Null and Empty Values

  • @NullSource
    • primitive data 는 널(null) 값을 허용할 수 없으므로 primitive 인수에 @NullSource 를 사용할 수 없다.
  • @EmptySource
    • 이 annotation 을 사용하여 빈 값을 인수에 전달할 수 있다.
    • String 인수의 경우 ““(empty string), Collection types 과 Arrays 에도 빈 값을 넣을 수 있다.
  • @NullAndEmptySource
    • null 값과 empty value 모두 전달하기 위해 해당 annotation 을 사용할 수 있다.
    • @EmptySource와 마찬가지로 문자열, 컬렉션 및 배열에서 동작한다.
    • 몇 가지 빈 문자열 변형을 parameterized test 로 전달하기 위해 @ValueSource, @NullSource 및 @EmptySource를 함께 결합할 수 있다.
        @ParameterizedTest
        @NullAndEmptySource
        void isBlank_ShouldReturnTrueForNullAndEmptyStrings(String input) {
        assertTrue(Strings.isBlank(input));
        }
      

3. Enum

  • @EnumSource
    • enumeration(열겨형) 값의 배열을 테스트 메서드에 전달한다.
    • test method 실행 당 하나의 인수(argument) 만을 전달할 때 사용할 수 있다.
    • names 속성
      • 리터럴 문자열 외에도 정규 표현식(regular expression)을 names 속성에 전달할 수 있다.
      • 기본적으로 names 속성은 일치하는 enum 값을 가진다.
      • mode 속성을 EXCLUDE 로 설정하면 제외하는 enum 값을 가질 수 있다.
  • e.g. 모든 월(Month enum)을 확인하여 월 숫자가 1과 12 사이에 있는지 확인하는 경우
      @ParameterizedTest
      @EnumSource(Month.class) // passing all 12 months
      void getValueForAMonth_IsAlwaysBetweenOneAndTwelve(Month month) {
          int monthNumber = month.getValue();
          assertTrue(monthNumber >= 1 && monthNumber <= 12);
      }
    
  • e.g. 월의 ‘이름’ 속성을 사용하여 특정 개월만 선택하는 경우 (4, 6, 9, 11월의 기간이 30일인지 확인)
      @ParameterizedTest
      @EnumSource(value = Month.class, names = {"APRIL", "JUNE", "SEPTEMBER", "NOVEMBER"})
      void someMonths_Are30DaysLong(Month month) {
          final boolean isALeapYear = false;
          assertEquals(30, month.length(isALeapYear));
      }
    
  • e.g. 2, 4, 6, 9, 11월을 제외한 월의 기간이 31일인지 확인하는 경우
      @ParameterizedTest
      @EnumSource(
        value = Month.class,
        names = {"APRIL", "JUNE", "SEPTEMBER", "NOVEMBER", "FEBRUARY"},
        mode = EnumSource.Mode.EXCLUDE)
      void exceptFourMonths_OthersAre31DaysLong(Month month) {
          final boolean isALeapYear = false;
          assertEquals(31, month.length(isALeapYear));
      }
    

4. Method

  • @MethodSource
    • 위의 argument sources들은 복잡한 object를 사용하여 전달하는 것이 어렵거나 불가능하다. 보다 복잡한 인수를 제공하는 방법 중 한 가지는 method를 argument source로 사용하는 것이다.
    • 즉, @MethodSource는 test method 실행 당 복잡한 인수 를 전달할 때 사용하는 방법이다.
      • test method 호출 당 하나의 인수(argument)를 전달하는 경우는 <Arguments> 추상화를 사용하지 않아도 된다.
    • @MethodSource 에 설정하는 이름은 존재하는 메서드 이름이어야 한다.
      • 이름을 설정하지 않으면 JUnit은 test method와 이름이 같은 source method를 찾는다.
    • argument source method 조건
      • Stream<Arguments> (혹은 Iterable, Iterator) 또는 List와 같은 컬렉션과 유사한 interface 를 반환할 수 있다.
      • 클래스 단위 생명 주기가 아닌 경우, static method 여야 한다.
    • @MethodSource를 이용하면 다른 테스트 클래스 간에 인수를 공유하는 것이 유용하다.
      • 이 경우 정규화된 이름 (FQN#methodName format)으로 현재 클래스 외부의 source method 를 참조할 수 있다.
  • e.g. 기본 사용 예제 - 복잡한 인수(String input, boolean expected)를 가짐
      @ParameterizedTest
      @MethodSource("provideStringsForIsBlank") // needs to match an existing method.
      void isBlank_ShouldReturnTrueForNullOrBlankStrings(String input, boolean expected) {
          assertEquals(expected, Strings.isBlank(input));
      }
    
      // a static method that returns a Stream of Arguments
      private static Stream<Arguments> provideStringsForIsBlank() { // argument source method
          return Stream.of(
            Arguments.of(null, true),
            Arguments.of("", true),
            Arguments.of("  ", true),
            Arguments.of("not blank", false)
          );
      }
    
  • e.g. 외부 source method 참조 예제
      class StringsUnitTest {
          @ParameterizedTest
          @MethodSource("com.baeldung.parameterized.StringParams#blankStrings") // 클래스 외부의 source method
          void isBlank_ShouldReturnTrueForNullOrBlankStringsExternalSource(String input) {
              assertTrue(Strings.isBlank(input));
          }
      }
    
      public class StringParams {
          static Stream<String> blankStrings() {
              return Stream.of(null, "", "  ");
          }
      }
    

관련된 Post

  • JUnit5의 기본적인 사용법에 대해 알고 싶으시면 JUnit5 기본 사용법을 참고하시기 바랍니다.

References


출처 : https://gmlwjd9405.github.io/2019/11/27/junit5-guide-parameterized-test.html