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

[Junit5] Assertions과 Assumptions - Assertions편

by 사바라다 2020. 5. 3.

안녕하세요. 오늘은 저번 포스팅에 이어서 JUnit5에서 제공하는 Assertions과 Assumptions에 대해서 조금더 디테일하게 알아보는 시간을 가져보도록 하겠습니다. 내용은 JUnit5 Guide에 나오는 내용을 기본으로 제 나름대로의 필요한 내용을 추가하는 식으로 진행하도록 하겠습니다.

Assertions는 번역하면 단정문이라고 합니다. assertions는 만약 성공하지 않으면 테스트를 실패처리를 하기 위해서 사용합니다.

Assumption은 번역하면 가정문이라고 합니다. assumptions는 특정 상황에서만 test문을 실행하고자 할때, 반대로 특정 상황에서만 실행하지 않고자할 때 사용하는 키워드입니다. 여기서 말하는 특정 상황이라는 것은 local환경 등을 들 수 있습니다.

Assertions의 JUnit4와의 가장 큰 차이점은 Java8의 람다를 사용할 수 있다라는 것입니다. 따라서 테스트 코드를 Functional 하게 작성할 수 있다라는 것이 가장 큰 차이점입니다.

전제 조건

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

오늘 테스트에 도움을 줄 Class는 2가지입니다. Calculator Class는 4칙연산을 담당하는 클래스이며, Person Class는 firstName과 lastName을 String 형태로 가지고 있습니다. 이후 나오는 테스트는 위 2개의 Class를 이용하여 테스트를 진행하겠습니다.

assertion 기본

JUnit5에서 기본 제공하는 assertion은 junit.jupiter.api.Assertions class에 있습니다. 대표적인 몇가지만 확인해보도록 하겠습니다.

@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.");

}

assertion에 대해서 출력되는 메시지를 붙일 수 있습니다. 실패하면 아래와 같이 메시지가 출력되게 됩니다. 아래는 위의 테스트 중 assertTrue를 실패했을 때 발생하는 에러입니다.

assertion group

assertion Group은 JUnit5에 추가된 기능으로 동시에 Assertion 구문을 실행시킬 수 있는 그룹을 만드는 것입니다. JUnit4에서는 여러개의 assertion 구문이 순차실행밖에 되지 않았었지만 JUnit5에 와서는 병행실행이 되게 된것입니다.

@Test
void groupedAssertions() {
    // In a grouped assertion all assertions are executed, and all
    // failures will be reported together.
    assertAll("person",
            () -> assertEquals("Jane", person.getFirstName()),
            () -> assertEquals("Doe", person.getLastName())
    );
}

이렇게 assertAll이라는 assertion 메서드와 람다식을 이용하여 병행실행을 구현할 수 있게 됩니다. 만약 두개의 테스트가 모두 실패한다면 아래와 같이 처리됩니다.

assertion_group 병행 실패

그리고 하나의 테스트만 실패한다면 실패한 테스트만 실패처리로 출력되게됩니다.

assertion_group 단건 실패

이러한 테스트 방법으로 인해 testAll 메서드를 이용하면 순차 테스트 뿐만 아닌 병행 테스트를 이용한 다양한 방식의 테스트가 가능해졌습니다.

@Test
void dependentAssertions() {
    // 코드 블럭안에서 만약 assertion 구문이 실패하면 아래 순차에 있는 코드는 실행되어지지 않습니다.
    assertAll("properties",
            () -> {
                String firstName = person.getFirstName();
                assertNotNull(firstName);

                // `assertNotNull(firstName)` 코드가 성공적이어야만 해당 코드는 실행됩니다.
                assertAll("first name",
                        () -> assertTrue(firstName.startsWith("J")),
                        () -> assertTrue(firstName.endsWith("e"))
                );
            },
            () -> {
                // 앞의 functional 코드에 상관없이 실행됩니다. 즉, 독립적으로 실행됩니다.
                String lastName = person.getLastName();
                assertNotNull(lastName);

                // `assertNotNull(lastName)` 코드가 성공적이어야만 해당 코드는 실행됩니다.
                assertAll("last name",
                        () -> assertTrue(lastName.startsWith("D")),
                        () -> assertTrue(lastName.endsWith("e"))
                );
            }
    );
}

위 코드를 아래와 같이 변경하게되면 첫번째 assertion에서는 assertNull 부분에서 오류가 날 것이며 두번째 assertion에서는 assertTrue 부분에서 오류가 날 것입니다.

assertAll("properties",
        () -> {
            String firstName = person.getFirstName();
            assertNull(firstName); // 오류 발생
        },
        () -> {
            assertAll("last name",
                    () -> assertTrue(lastName.startsWith("Z"))
            ); // 오류 발생
        }
);

오류는 동시에 발생할 것이며 아래와 같이 오류가 출력됩니다.

exception

JUnit4에서의 Exception 테스트는 @Test(expected = "<Exception Class Name>") Annotation을 통해서 할 수 있었습니다. 하지만 이런 exception 테스트는 exception이 발생하는 지점을 정확하게 파악하기 힘들다는 단점이 있었습니다.

JUnit5에서는 assertThrows를 이용하여 exception 테스트가 가능합니다. exception이 발생할 수 있는 부분을 람다식을 이용하여 감쌈으로써 exception이 발생하는 지점을 특정하여 테스트가 가능합니다.

@Test
void exceptionTesting() {
    Exception exception = assertThrows(ArithmeticException.class, () ->
            calculator.divide(1, 0));
}

아래와 같이 exception이 발생하지 않는 코드를 작성 후 테스트 하면 exception이 발생하지 않았다고 fail이 발생합니다.

@Test
void exceptionTesting() {
    Exception exception = assertThrows(ArithmeticException.class, () ->
            calculator.divide(1, 1));
}
org.opentest4j.AssertionFailedError: Expected java.lang.ArithmeticException to be thrown, but nothing was thrown.

timeout

JUnit5의 Timeout과 관련하여 추가된 assertion을 보도록 하겠습니다. Timeout 역시 JUnit4에서는 @Test 어노테이션에 timeout 파라미터를 넣어 처리하였습니다. @Test(timeout=100) 이런식으로 말이죠. 따라서 exception과 동일한 exception 발생 위치를 확인할 수 없다는 문제점을 가지고 있습니다.

JUnit5에서는 assertTimeout 이라는 메서드로 이러한 문제점을 해결할 수 있습니다. 사용방법은 아래와 같습니다. 해당 메서드는 반환값을 가질 수 있기 때문에 반환한 값을 후에 이용할 수 도 있습니다.

@Test
void timeoutNotExceeded() {
    // The following assertion succeeds.
    assertTimeout(ofMinutes(2), () -> {
        // Perform task that takes less than 2 minutes.
    });
}

@Test
void timeoutNotExceededWithResult() {
    // The following assertion succeeds, and returns the supplied object.
    String actualResult = assertTimeout(ofMinutes(2), () -> {
        return "a result";
    });
    assertEquals("a result", actualResult);
}

마무리

오늘은 JUnit5에서 Assertions과 Assumptions의 기본적인 차이점과 Assertions에 대해서 알아보는 시간을 가졌습니다.

다음 시간에는 Assumptions에 대해서 알아보는 시간을 가져보도록 하겠습니다.

도움이 되셨다면 하트 또는 광고 클릭 부탁드리겠습니다.

감사합니다.

참조

junit5 guide

assertion vs assumption

댓글