JUnit 5 (4)

들어가기 전에

공식 유저 가이드 의 3장, 테스트 작성법 일부 를 정리하였다.

@Test 와 기본적인 테스트

@Test 는 가장 기본적인 어노테이션으로서, 테스트 메서드를 지정할 수 있다. JUnit 4 시절과 달리, 사용 가능한 옵션도, 추가적인 인자도 정의되어 있지 않다.

import static org.junit.jupiter.api.Assertions.fail;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

class StandardTests {

    @BeforeAll
    static void initAll() {
    }

    @BeforeEach
    void init() {
    }

    @Test
    void succeedingTest() {
    }

    @Test
    void failingTest() {
        fail("a failing test");
    }

    @Test
    @Disabled("for demonstration purposes")
    void skippedTest() {
        // not executed
    }

    @AfterEach
    void tearDown() {
    }

    @AfterAll
    static void tearDownAll() {
    }

}

테스트 이름

@DisplayName 어노테이션을 사용하여 테스트 클래스와 메서드에 이름을 붙여줄 수 있다.

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

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

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

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

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

}

검증

검증Assertion 메서드는 JUnit 4 에서 친숙하게 보아왔던 것들이 거의 그대로 넘어왔고, 람다 지원을 위한 몇 가지 메서드들이 추가되었다. 자세한 내용은 여기 에서 확인 가능하다.

JUnit 5는 Java 8의 추가 기능을 받아들이며, 사용자가 테스트를 작성할 때 람다 를 적극적으로 사용하도록 유도하였다. 기존에는 테스트 메서드를 생성해야 할 수 있던 일을, JUnit 5에서는 람다가(그리고 우리는, 람다를 익명 함수 라고 읽을 수도 있다.) 할 수도 있다. 아래 코드는 예제 코드에서 그러한 부분만 추출한 것이다.

import static java.time.Duration.ofMillis;
import static java.time.Duration.ofMinutes;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTimeout;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

class AssertionsDemo {
    // 기본적으로는 람다를 활용하여 여러 개의 테스트 케이스를 검증할 수 있다.
    @Test
    void groupedAssertions() {
        // In a grouped assertion all assertions are executed, and any
        // failures will be reported together.
        assertAll("person",
            () -> assertEquals("John", person.getFirstName()),
            () -> assertEquals("Doe", person.getLastName())
        );
    }

    // JUnit 4에서는 테스트 메서드 외부(@Test 어노테이션의 인자)에서 정의해야 했던 여러 '옵션'도, 람다를 활용하여 해결하고 있다.
    @Test
    void exceptionTesting() {
        Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
            throw new IllegalArgumentException("a message");
        });
        assertEquals("a message", exception.getMessage());
    }

    // 람다로부터 결과값 또한 확인할 수 있다.
    @Test
    void timeoutNotExceededWithResult() {
        // The following assertion succeeds, and returns the supplied object.
        String actualResult = assertTimeout(ofMinutes(2), () -> {
            return "a result";
        });
        assertEquals("a result", actualResult);
    }
}

추정

검증 메서드들과 같이, 추정Assumption 메서드 또한 JUnit 4의 친숙한 모습을 거의 가져왔으며, 람다 지원을 위한 약간의 변경사항이 있다. 자세한 사항은 여기 에서 확인 가능하다.

@ParameterizedTest 와 인자를 제공받는 테스트

@Test 어노테이션과 달리, @ParameterizedTest 어노테이션은 테스트 메서드가 인자를 받을 수 있도록 한다.

@Test
void test() {
    fail();
}

// 위 테스트 메서드와 비교하라.
@ParameterizedTest
void testWithStringParameter(String argument) {
    assertNotNull(argument);
}

인자를 제공받는 테스트 메서드는 인자를 획득하기 위해 @ValueSource 등의 어노테이션을 추가로 요구한다. 아래는 그 예시이다.

// @ValueSource 어노테이션은 문자열, 정수 등, 원시 자료형(primitive types)의 배열을 메서드에 전달할 수 있다.
@ParameterizedTest
@ValueSource(strings = { "Hello", "World" })
void testWithStringParameter(String argument) {
    assertNotNull(argument);
}

JUnit이 제공할 수 있는 인자의 종류와 제공 방법은 다양하며, 사용 가능한 어노테이션은 아래와 같다.

어노테이션 설명
@ValueSource 테스트 메서드에 원시 자료형 배열을 제공한다.
@EnumSource 테스트 메서드에 열거형 배열을 제공한다. 특정 열거형 타입으로부터 모든 객체를 가져오거나, 일부 객체만 가져올 수도 있으며, 옵션에 따라 일부 제외 및 정규표현식을 이용한 선택이 가능하다.
@MethodSource 테스트 메서드가 요구하는 인자 타입의 스트림Stream(혹은 Iterable, Iterator)을 반환하는 메서드를 지정하여 인자를 제공한다. 스트림을 제공하는 메서드는 클래스 단위 생명주기가 아닌 경우, 정적 메서드여야 한다.
@CsvSource 이 어노테이션은 쉼표로 분리된 데이터(문자열)를 가지는 배열을 테스트 메서드에 제공한다. 테스트 메서드는 한 개 이상의 인자를 제공받을 수 있다.
@CsvFileSource 이 어노테이션은 CSV 파일을 사용하여 테스트 메서드에 인자를 제공한다.
@ArgumentsSource 이 어노테이션은 @MethodSource 어노테이션과 유사한 동작을 한다. 차이점은, 인자 제공자Provider가 메서드 대신 재사용 가능한 정적 클래스라는 것이다.

@RepeatedTest 와 반복 테스트

@RepeatedTest 어노테이션은 특정 테스트 케이스를 반복 실행할 수 있도록 한다.

// 이 테스트 케이스는 열 번 실행된다.
@RepeatedTest(10)
void repeatedTest() {
    // ...
}