JUnit5 @ParameterizedTest

테스트 코드를 작성하던 중, 한 기능에서 로직은 같지만 입력 파라미터만 다른 테스트케이스를 작성해야 할 일이 있었다. 코드는 중복되지만, 각 케이스에 대한 검증은 필요하므로 테스트 코드를 작성하지 않을 수는 없다.


이러한 경우 조금 더 코드를 간결하게 작성하는 방법이 있지 않을까 고민해보고 찾아보는 중 Junit5에서 제공하는 @ParameterizedTest 라는 것을 알게 되었다.



@ParameterizedTest

@ParameterizedTest는 하나의 테스트를 여러 번 돌려야 할 때 사용한다.

@ParameterizedTest 적용 방법

  • 메소드 위에 @ParameterizedTest를 명시한다.
  • 메소드의 파라미터로 순서대로 넘겨줄 배열을 @ValueSource를 이용해 명시해준다. (short, byte, int, long, float, double, char, String, Class 명시 가능)
  • 넘겨줄 값들의 타입을 메소드의 파라미터로 적어준다.

예시

import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class TestControllerTest {

  @ParameterizedTest
  @ValueSource(strings = {"test1", "test2", "test3"})
  void test(String value) {
    System.out.println(value);
  }
}

이 테스트 코드의 실행 결과는 다음과 같다.


하지만, 이 방법은 파라미터를 하나만 전달할 때만 가능하며 두 개 이상의 파라미터는 전달할 수 없다.





@MethodSource

@MethodSource를 이용하면 두 개 이상의 파라미터 또한 전달받을 수 있도록 할 수 있다.

예시

import java.util.stream.Stream;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class TestControllerTest {

  // test method
  @ParameterizedTest
  @MethodSource("provideKeyAndValue")
  void test(Integer key, String value) {
    System.out.println(key + " " + value);
  }

  //source method
  private static Stream<Arguments> provideKeyAndValue() {
    return Stream.of(
        Arguments.of(1, "value1"),
        Arguments.of(2, "value2"),
        Arguments.of(3, "value3")
    );
  }
}

단, 이를 사용하기 위해서는 source method가 static이어야 한다.


하지만, 성격이 같은 테스트케이스끼리 묶을 경우 종종 inner class를 사용하는 경우가 있고, inner class에서는 static method를 선언하지 못한다.

import java.util.stream.Stream;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class TestControllerTest {

  @Nested
  public class NestedTestControllerTest {

    @ParameterizedTest
    @MethodSource("provideKeyAndValue")
    public void test(Integer key, String value) {
      System.out.println(key + " " + value);
    }
    
    // 에러 발생!
    private static Stream<Arguments> provideKeyAndValue() {
      return Stream.of(
          Arguments.of(1, "value1"),
          Arguments.of(2, "value2"),
          Arguments.of(3, "value3")
      );
    }
  }
}

이렇게 불가능하다는 뜻이다... 이럴 때는 @TestInstance를 사용하면 되는데, 이제부터 @TestInstance에 대해 알아볼 예정이다.



@TestInstance

@TestInstance는 테스트 인스턴스의 라이프 사이클을 설정할 때 사용한다.

  • Lifecycle.PER_METHOD (Default) : 테스트 함수 당 인스턴스가 생성된다.
  • Lifecycle.PER_CLASS : 테스트 클래스 당 인스턴스가 생성된다.

import java.util.stream.Stream;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class TestControllerTest {

  @Nested
  @TestInstance(Lifecycle.PER_CLASS)
  public class NestedTestControllerTest {

    @ParameterizedTest
    @MethodSource("provideKeyAndValue")
    public void test(Integer key, String value) {
      System.out.println(key + " " + value);
    }

    private Stream<Arguments> provideKeyAndValue() {
      return Stream.of(
          Arguments.of(1, "value1"),
          Arguments.of(2, "value2"),
          Arguments.of(3, "value3")
      );
    }
  }
}

이렇게 @TestInstance를 Lifecycle.PER_CLASS로 설정해주면 동일한 test instance에서 모든 test method를 실행할 수 있게 되며 provideKeyAndValue()를 static으로 두지 않고도 @MethodSource를 사용할 수 있게 된다.




사실 위와 같이 Unit Test를 작성해도 괜찮을지는 모르겠지만, 한 번도 접해보지 못한 어노테이션을 사용하였고 알게 되었다는 것에 의의를 둘 수 있었다.

좋은 웹페이지 즐겨찾기