ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] junit5 설명 및 기본 사용 - junit4 와 5 비교
    Java/Java 기본 및 이론 2021. 1. 11. 11:55

    JUnit 5란 
    문서에서는 JUnit 5는 크게 세가지 서브 프로젝트의 여러 모듈로 구성이 되어있다고 한다.

    JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintag -e

    • Junit Platform

      JVM에서 테스트를 하기 위한 기초적인 역할을 담당

      • 테스트를 하기 위한 TestEngine API 제공

      • JUnit 4(하위 버전) 기반 테스트 제공
    • Junit Jupiter

      JUnit 5에서 테스트 및 확장하기 위한 프로그래밍 모델과 확장 모델의 조합이다.
      Platform에서 사용하는 TestEngine은 Jupiter를 통해 제공하는 것이다.

      Jupiter API 는 Junit5 에 새롭게 추가된 API 들을 포함하고 있다.
    • Junit Vintage

      하위 버전들(JUnit 3/4) 기반의 테스트를 실행시기키 위해 해당 TestEngine을 제공한다.

     

     문서 설명을 보면 Platform이 가장 상단에서 개발자들에게 JUnit의 전반적인 테스트 API들을 제공하며, 이를 실제적으로 구현한 Jupiter와 Vintage가 존재한다라고 할 수 있다.

    JUnit 5를 사용하기 위해서는 런타임에 Java 8 이상이 필요하나, 이전 버전의 JDK로 컴파일 된 코드들 또한 테스트가 가능하다.

     

    참고로 Spring Boot 2.2.0 버전부터 JUnit5가 기본으로 채택되었다.


    Spring Initializer를 통해 프로젝트를 만들어보면 Test 디펜던시가 다음과 같이 되어있는것을 볼 수 있습니다....

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <exclusions>    
        <exclusion>
          <groupId>org.junit.vintage</groupId>
          <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

    junit-vintage-engine 모듈을 제외하고 있습니다. vintage-engine 자체가 JUnit4를 지원하기 위한 모듈이다보니 기본적으로는 제외하도록 설정되는 것입니다.

    만약 JUnit4 테스트케이스를 같이 관리해주어야하는 상황이라면 위 exclusion 설정을 삭제하면 된다.

     

    Annotations

    Jupiter에서는 테스트와 확장된 기능을 제공하기 위해 여러가지 어노테이션들을 제공한다.

    Annotaion설명

    @Test 테스트 메소드임을 알림
    Junit 4와는 다르게 속성을 정의 X
    이는 Jupiter에선 이미 해당 어노테이션들이 존재하기 때문
    @ParameterizedTest 여러가지 매개변수를 통해 다양한 테스트 진행
    @RepeatedTest 반복 횟수만큼 테스트를 진행
    @TestFactory 동적 테스트를 위한 테스트 팩토리
    @TestTemplate 일반 테스트가 아닌 테스트 케이스의 템플릿
    @TestMethodOrder 테스트 메서드의 실행 순서를 구성하는데 사용(Junit 4의 @FixMethodOrder와 유사)
    @TestInstance 테스트 인스턴스 생명주기를 구성하는데 사용
    @DisplayName 테스트 클래스 혹은 메소드에 대한 이름을 선언
    @DisplayNameGeneration 테스트 클래스에 대한 Display name generator를 선언
    @BeforeEach 현재 클래스에서 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory가 적힌 각각의 메소드들 보다 먼저 실행
    (JUnit 4의 @Before와 동일)
    @AfterEach 현재 클래스에서 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory가 적힌 각각의 메소드들 보다 나중에 실행
    (JUnit 4의 @After와 동일)
    @BeforeAll 현재 클래스에서 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory가 적힌 모든 메소드들 보다 먼저 실행
    (JUnit 4의 @BeforeClass와 동일)
    @AfterAll 현재 클래스에서 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory가 적힌 모든 메소드들 보다 나중에 실행
    (JUnit 4의 @AfterClass와 동일)
    @Nested 중첩된 테스트 클래스임을 알림
    각 클래스의 테스트 인스턴스 생명주기를 사용하지 않는 한 @BeforeAll과 @AfterAll 메소드는 사용 X
    @Tag 테스트 필더링을 위한 테그를 선언하는데 사용
    @Disabled 테스트 클래스 혹은 메소드를 비활성하는데 사용(JUnit 4의 @Ignore와 유사)
    @Timeout 주어진 시간을 초과할 경우, 테스트 실패를 나타내기 위해 사용
    @ExtendWith 확장을 선언적으로 등록하는데 사용
    @RegisterExtension 필드를 통해 프로그래밍 방식으로 확장을 등록하는데 사용
    @TempDir 필드 주입 또는 매개변수 주입을 통해 임시 디렉토리를 제공하는데 사용

     

    @DisplayName

    기존에 테스트 결과가 보고될 때 메소드명이 테스트케이스 이름으로 사용되었는데요. 때문에 테스트케이스를 잘 설명하기 위해 한글로 하고는 했습니다.

    JUnit5에서는 @DisplayName 어노테이션을 활용해 좀 더 깔끔하게 이름을 붙혀줄 수 있습니다.

    public class MemberServiceTest {
    
      // 기존 방식
        @Test
        public void 멤버_프로필_조회() {
            ...
        }
    
      // JUnit5 방식
      @Test
      @DisplayName("멤버 프로필 조회")
      public void getMemberProfileTest() {    
            ...    
      }
    }

     

     LifeCycle Hook

    JUnit 테스트케이스 작성 시 개발자가 테스트 수행 전 후 특정 시점에 실행될 동작을 정의할 수 있다.

    기존에는 @BeforeClass, @AfterClass, @Before, @After 어노테이션을 활용했는데.

    JUnit5에서는 @BeforeAll, @AfterAll, @BeforeEach, @AfterEach 로 변경되었다. @Before와 @After는 기존에 (테스트케이스 마다)라는 의미가 없어서 혼동의 여지가 있었지만, 명확하게 네이밍이 변경되었다.

     

    assertions

    Assertion을 프로그래밍 관점에서 해석하면 표명, 가정 설정문으로 할 수 있으며, 이를 통해 자신의 로직이 정확한지 테스트 해보는 것이다.
    Jupiter에서는 기존 버전에 존재했던 Assertion 메소드를 포함하고, 람다와 함께 사용하기 적합한 추가적인 메소드를 제공한다.
    모든 Assertion은 정적 메소드로 정의되어 있다.

     

    테스트에서 assertion종류가 많고 복잡한 경우 assertAll(Executable...executables)을 이용할 수 있습니다.
    Executable은 파라미터와 반환값이 없는 함수형 인터페이스이기 때문에 assertAll로 그룹핑 + assertion을 람다로 작성할 수 있습니다.

    assertAll("assertionGroup1",
      () -> assertEquals(expected, actual),
      ...    
    )
    @Test
    public void shouldFailBecauseTheNumbersAreNotEqual_lazyEvaluation() {
        Assertions.assertTrue(
          2 == 3,
          () -> "Number 2 and 3 are not equal!");
    }
    @Test
    public void shouldAssertAllTheGroup() {
        List<Integer> list = Arrays.asList(1, 2, 4);
        Assertions.assertAll("List is not incremental",
        () ->  Assertions.assertEquals(list.get(0).intValue(), 1),
        () ->  Assertions.assertEquals(list.get(1).intValue(), 2),
        () ->  Assertions.assertEquals(list.get(2).intValue(), 3));
     }

    @ParameterizedTest

    한 메소드에서 여러 종류의 파라미터를 받아 테스트를 수행할 수 있습니다.

    @DisplayName("공백 문자열 테스트")
    @ParameterizedTest(name = "문자열 {0}으로 조회")
    @ValueSource(strings = " ", "")
    public void isBlankStringTest(String source) {
      boolean isBlank = service.isBlank(source);
      assertTrue(isBlank);
    }

    참고로 @ParamterizedTest를 사용하려면 junit-jupiter-params 모듈을 추가해야합니다.

    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-params</artifactId>
      <version>VERSION_TO_USE</version>
      <scope>test</scope>
    </dependency>

    Assumptions

    Assumption은 테스트를 진행할지 안할지에 대해 테스터가 작성한 가정을 기반으로 선택된다.
    즉, 설정한 가정이 참일 경우에는 테스트를 실행하고 거짓일 겅우에는 테스트는 실행하지 않는다.
    이러한 Assumption은 조그마한 단위 테스트보다는 통합 테스트에 더 적절히 사용할 수 있다.
    Assertion과 똑같이 정적 메소드로 정의되어 있다.

    @Test
    void testOnlyOnDev() {
      assumeTrue("DEV".equals(System.getenv("ENV")));
      
      DEV 환경에서만 테스트할 코드;
    }
    
    @Test
    void testAllEnv() {
      assumeTrue("DEV".equals(System.getenv("ENV")),
                 () -> {
                   DEV 환경에서 테스트할 코드;
                 });
    
      모든 환경에서 테스트할 코드;
    }

    Tagging And Filtering

    Junit 4 에서는 @Category Annotation 을 사용하여 테스트를 그룹화 할 수 있다. Junit 5 에서는 @Category Annotation 이 @Tag Annotation 으로 대체된다.

    @Tag("annotations")
    @Tag("junit5")
    @RunWith(JUnitPlatform.class)
    public class AnnotationTestExampleTest {
    	/* */
    }

     

     

     

    참고문헌

     https://junit.org/junit5/docs/current/user-guide/#overview [공식문서]

     

    반응형

    댓글

Designed by Tistory.