본문 바로가기
프로그래밍/테스트

[Junit5] Junit4에서 Junit5으로

by 사바라다 2020. 5. 1.

안녕하세요. Spring Boot 2.2.x는 Junit5를 기본으로 제공하고 있습니다. 메이저 버전이 바뀌게 되었습니다. 과연 Junit4에서 Junit5로 변화하면서 어떤 부분이 변경되었을까요? 오늘은 Junit5를 사용하는 방법과 추가된 점에 대해서 알아보도록 하겠습니다.

JUnit5

JUnit5는 차세대 Java Test 프레임워크로 3가지의 서브 패키지로 구성되어 있습니다.

JUnit5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

  • JUnit Platform : JUnit Platform은 JVM에서 테스트 프레임워크를 시작하기 위한 기초적인 역할을 수행합니다. 또한 테스트 개발을 위한 API를 제공합니다.
  • JUnit Jupiter : JUnit5에서 테스트 및 Extension을 작성하기 위한 새로운 프로그래밍 모델과 확장 모델의 조합, TestEngine제공
  • JUnit Vintage : 하위 호환성을 위해서 JUnit4, JUnit3를 실행할 수 있는 TestEngine입니다.

사용법

JUnit5를 사용하려면 최소한 JDK8 버전 이상을 사용하고 있어야합니다.

Spring Boot 2.2.x 이후

Spring Boot 2.2.x 이후 버전에서는 JUnit5를 기본적으로 제공하고 있습니다. 따라서 org.springframework.boot:spring-boot-starter-test를 추가하여 사용하고 있습니다. 만약 JUnit4에 대한 호환성을 제거하고 JUnit5만 사용하겠다면 org.junit.vintage:junit-vintage-engine 모듈을 exclude 해주시면 됩니다.

testImplementation("org.springframework.boot:spring-boot-starter-test")

test {
    useJUnitPlatform()
}

Spring Boot 2.2.x 이전

Spring Boot 2.1.x 버전까지는 기본으로 제공하고 있던 Test 프레임워크는 JUnit4였습니다. 따라서 spring-boot-starter-test 모듈을 주입하고 JUnit4 모듈은 exclude 명령어로 제외하도록 합니다. 그리고 junit-jupiter 모듈들을 의존하면 JUnit5를 사용할 수 있게 됩니다.

testImplementation("org.springframework.boot:spring-boot-starter-test") {
    exclude module : 'junit'
}

testImplementation("org.junit.jupiter:junit-jupiter-api")
testCompile("org.junit.jupiter:junit-jupiter-params")
testRuntime("org.junit.jupiter:junit-jupiter-engine")

test {
    useJUnitPlatform()
}

테스트의 Class와 Method

Test 역시 자바의 객체로 구성되게 됩니다. 테스트를 위한 최소 조건으로 Class와 그 내부 method가 필요합니다. 각각 요소에 대해서 알아보도록 하겠습니다.

  • Class : 최상위 클래스, static memeber class, @Nested class 중 적어도 하나의 test method를 포함하고 있는 클래스

Test class는 abstract 클래스여선 안되며 단일 생성자 (constrcuctor() {})를 포함하여야 합니다.

  • Test Method : @Test, @RepeatedTest, @ParameterizedTest, @TestFactory, @TestTemplate이 선언되어있는 메서드로 실제 테스트하고자 하는 테스트를 넣는 클래스
  • Lifecycle Method : @BeforeAll, @AfterAll, @BeforeEach, @AfterEach가 선언되어있는 메서드로 Test의 라이프사이클에 따라 실행되는 메서드

test method와 lifecycle method는 public이어야합니다.

라이프사이클 메서드

JUnit4와 마찬가지로 JUnit5에도 라이프사이클 메서드가 존재합니다. 4개의 어노테이션이 존재하는데 각각에 대해서 알아보도록 하겠습니다.

  • @BeforeAll
    • 테스트 Class 기준으로 테스트 메서드들이 실행되기전 실행
    • JUnit4의 @BeforeClass 역할
  • @BeforeEach
    • 각 테스트 메서드가 실행되기전 실행
    • JUnit4의 @Before 역할
  • @AfterAll
    • 테스트 Class 기준으로 테스트 메서드들이 실행된 후 실행
    • JUnit4의 @AfterClass 역할
  • @AfterEach
    • 각 테스트 메서드가 실행된 후 실행
    • JUnit4의 @After 역할

전체적인 라이프사이클은 BeforeAll -> BeforeEach -> Test -> AfterEach -> AfterAll 입니다. 아래코드는 테스트 코드를 한번 작성해 보았습니다.

public class StandardTest {

    @BeforeAll
    static void beforeAll() {
        System.out.println("BeforeAll method call");
    }

    @BeforeEach
    void beforeEach() {
        System.out.println("");
        System.out.println("BeforeEach method call");
    }

    @Test
    void succeedingTest() {
        System.out.println("succeedingTest method call");
    }

    @Test
    void succeedingTest_2() {
        System.out.println("succeedingTest_2 method call");
    }

    @AfterEach
    void afterEach() {
        System.out.println("afterEach method call");
    }

    @AfterAll
    static void afterAll() {
        System.out.println("afterAll method call");
    }
}

이렇게 코드를 작성후 test Suites를 실행해보도록 하겠습니다. 결과는 아래와 같습니다. 전체적인 라이프사이클과 동일하다는 것을 알 수 있었습니다.

BeforeAll method call

BeforeEach method call
succeedingTest method call
afterEach method call

BeforeEach method call
succeedingTest_2 method call
afterEach method call
afterAll method call

Display Names

JUnit4에서는 reports와 IDE test runner에 출력도는 이름은 method의 이름이었습니다. 따라서 메서드의 이름이 기어지는 단점이 있었죠. 아래 처럼요. 아래는 Place를 가져오지만 NullPointerException이 발생했을 때를 나타낸 것입니다.

@Test
public void getPlaces_NullpointerException_발생() {}

JUnit5에서는 @DisplayName 어노테이션을 입력하면 reports와 IDE의 test runner에 출력되는 이름을 지정할 수 있습니다.

@Test
@DisplayName("getPlace, NullPointerException 발생")
public void getPlaces_exception() {}

@DisplayName을 이용하면 다양한 문자를 포함할 수 있습니다. 아래는
JUnit5 User Guide에 나와있는 예제입니다.

java
@DisplayName("A special test case")
class DisplayNameDemo {

    @Test
    @DisplayName("Custom test name containing spaces")
    void testWithDisplayNameContainingSpaces() {
    }

    @Test
    @DisplayName("╯°□°)╯")
    void testWithDisplayNameContainingSpecialCharacters() {
    }

    @Test
    @DisplayName("😱")
    void testWithDisplayNameContainingEmoji() {
    }

}

만약 출력이 되지 않는다면 Intellij의 설정을 변경해주어야 합니다. Preferences -> Build, Execution, Displayment -> Build Tools -> Gradle에 들어가셔서 Run tests using을 Intellij IDEA로 변경해주시고 재실행해주시면 됩니다.

Assertions

JUnit5에는 JUnit4의 다양한 assertion method와 함께 Java8의 lambda식과 함게 사용하기 적합한 몇가지가 추가되어있습니다. Jupiter Assertion은 모두 org.junit.jupiter.api.Assertions에 static으로 포함되어 있습니다.

아래는 JUnit5에서 사용할 수 있는 Assertions 구문들의 기본입니다. JUnit5 User Guide에서 일부 발췌하였습니다.

class AssertionsDemo {

    private final Calculator calculator = new Calculator();

    private final Person person = new Person("Jane", "Doe");

    @Test
    void standardAssertions() {
        assertEquals(2, calculator.add(1, 1));
        assertEquals(4, calculator.multiply(2, 2),
                "The optional failure message is now the last parameter");
        assertTrue('a' < 'b', () -> "Assertion messages can be lazily evaluated -- "
                + "to avoid constructing complex messages unnecessarily.");
    }

    @Test
    void groupedAssertions() {
        // assetions를 그룹화 하여 실행합니다.
        // 실패의 경우가 있더라도 그룹에 있는 assertion 구문은 모두 실행됩니다.

        assertAll("person",
            () -> assertEquals("Jane", person.getFirstName()),
            () -> assertEquals("Doe", person.getLastName())
        );
    }

    @Test
    void exceptionTesting() {
        // exception 테스트도 함수화하여 사용할 수 있게 되었습니다.
        Exception exception = assertThrows(ArithmeticException.class, () ->
            calculator.divide(1, 0));
        assertEquals("/ by zero", exception.getMessage());
    }

    @Test
    void timeoutNotExceeded() {
        // timeout의 경우도 함수화하여 사용할 수 있게 되었습니다.
        assertTimeout(ofMinutes(2), () -> {
            // Perform task that takes less than 2 minutes.
        });
    }

    private static String greeting() {
        return "Hello, World!";
    }
}

JUnit5가 정식으로 지원하는 jupiter assertion library 이외에도 hamcrest 서드파티 assertion library도 사용할 수 있습니다.

2.6. Disabling

JUnit4의 @Ignore와 마찬가지로 JUnit5에서는 @Disabled를 사용할 수 있다. class또는 method에 사용할 수 있으며 사용처에 따라서 범위가 달라집니다. 추가적으로 코멘트를 달 수 도 있습니다.

@Disabled("Disabled until bug #99 has been fixed")
class DisabledClassDemo {

    @Test
    void testWillBeSkipped() {
    }

}

마무리

오늘은 이렇게 JUnit5에 대해서 기본적인 사용방법에 대해서 알아보는 시간을 가졌습니다.

추가적인 내용은 다음번 포스팅에 올리도록 하겠습니다.

감사합니다.

참조

JUnit5 User Guide

댓글