티스토리 뷰

Test

JUnit5 (1/2)

Jaime.Lee 2019. 11. 26. 17:04
모든 소스는 여기서 확인하실 수 있습니다.

개요

JUnit은 자바 생태계에서 가장 유명한 단위 테스트 프레임워크 입니다. 특히 JUnit5 버전에는 다양한 스타일의 테스트가 가능할뿐만 아니라 Java 8 버전 이상의 새로운 기능을 지원하기위한 여러 가지 혁신적인 기능이 포함되어 있습니다.

의존관계

- maven

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.5.2</version>
    <scope>test</scope>
</dependency>

- gradle

testCompile 'org.junit.jupiter:junit-jupiter-engine:5.5.2'
2019년 9월에 release된 5.5.x 버전을 사용하였습니다.
자바 8버전 이상에서만 동작합니다.

아키텍처

JUnit5는 세 가지 하위 프로젝트 모듈로 구성되어 있습니다.

JUnit Platform

플랫폼은 JVM에서 테스트 프레임 워크를 시작합니다. JUnit과 빌드 도구와 같은 클라이언트 간의 안정적이고 강력한 인터페이스를 정의합니다.

최종 목표는 클라이언트가 JUnit과 쉽게 통합될 수 있도록 테스트를 발견하게 실행하는 데 있습니다.


또한 JUnit 플랫폼에서 실행되는 테스트 프레임 워크를 개발하기위한 TestEngine API를 정의합니다. 이를 통해 사용자 정의 TestEngine을 구현하여 써드 파티 테스트 라이브러리를 JUnit에 직접 플러그인 할 수 있습니다.

JUnit Jupiter

이 모듈에는 JUnit 5에서 테스트를 작성하기위한 새로운 프로그래밍 및 확장 모델이 포함되어 있습니다. JUnit 5에서 새로 추가된 애노테이션은 다음과 같습니다.

 

  • @TestFactory – 동적 테스트를위한 테스트 팩토리 메소드를 나타냄
  • @DisplayName – 테스트 클래스 또는 테스트 메소드의 사용자 정의 표시 이름을 정의
  • @Nested – 주석이 달린 클래스가 중첩 된 비 정적 테스트 클래스임을 나타냄
  • @Tag – 필터링 테스트를 위한 태그
  • @ExtendWith – 사용자 정의 확장명을 등록
  • @BeforeEach – 애노테이션이 있는 메소드가 각 테스트 메소드 전에 실행됨 (이전 버전의 @Before)
  • @AfterEach – 애노테이션이 있는 메소드가 각 테스트 메소드 후에 실행 (이전 버전의 @After).
  • @BeforeAll – 애노테이션이 있는 메소드가 현재 클래스의 모든 테스트 메소드보다 먼저 실행 (이전 버전의 @BeforeClass)
  • @AfterAll – 애노테이션이 있는 메소드가 현재 클래스의 모든 테스트 메소드보다 나중에 실행 (이전 버전의 @AfterClass)
  • @Disable – 테스트 클래스 또는 메소드를 비활성화 (이전 버전의 @Ignore)

JUnit Vintage

JUnit5 플랫폼에서 JUnit3 및 JUnit4 기반 테스트 실행을 지원합니다.

기본 애노테이션

@BeforeAll, @BeforeEach

package io.lcalmsky.junit;

import lombok.extern.java.Log;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;

@Log
public class BeforeTests {
    @BeforeAll
    public static void setup() {
        log.info("@BeforeAll");
    }

    @BeforeEach
    public void init() {
        log.info("@BeforeEach");
    }
}
@BeforeAll은 반드시 static으로 선언되어야 합니다.

@DisplayName, @Disabled

표시되는 이름을 바꾸거나 테스트를 실행하지 않도록 합니다.

package io.lcalmsky.junit;

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

@DisplayName("title")
public class DisplayNameAndDisabledTests {
    @Test
    @DisplayName("I think this is most useful")
    public void test() {
        
    }

    @Test
    @Disabled("Not implemented yet")
    public void testNotImplemented() {

    }
}

@AfterEach, @AfterAll

package io.lcalmsky.junit;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;

public class AfterTests {

    @AfterAll
    public static void done() {
        // 모든 테스트가 끝난 후 한 번 실행됨
    }

    @AfterEach
    public void teardown() {
        // 테스트 후 매 번 실행됨
    }
}
@AfterAll은 반드시 static으로 선언되어야 합니다.

Assertions, Assumptions

JUnit5는 Java 8의 새로운 기능, 특히 람다 식을 최대한 활용하고자 합니다.

 

Assertions

org.junit.jupiter.api.Assertions 패키지로 이동하면서 람다 표현식이 추가되었습니다.

다양한 메서드 시그니처를 제공하기 때문에 편한 방식으로 사용하셔도 큰 무리는 없을 것 같습니다.

보다 복잡한 형태의 Assertion이 메소드내에서 가능해졌습니다.

package io.lcalmsky.junit;

import org.junit.Test;

import java.util.stream.Stream;

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

public class AssertionTests {
    @Test
    public void originalExpression() {
        assertTrue(Stream.of(1, 2, 3)
                .mapToInt(i -> i)
                .sum() > 5);

    }

    @Test
    public void booleanSupplier() {
        assertTrue(() -> Stream.of(1, 2, 3)
                .mapToInt(i -> i)
                .sum() > 5
        );
    }

    @Test
    public void lambdaExpressions() {
        assertTrue(
                () -> Stream.of(1, 2, 3)
                        .mapToInt(i -> i)
                        .sum() > 5,
                () -> "sum should be greater than 5");
    }

    @Test
    public void groupAssertions() {
        int[] numbers = {0, 1, 2, 3, 4};
        assertAll("numbers",
                () -> assertEquals(numbers[0], 1),
                () -> assertEquals(numbers[3], 3),
                () -> assertEquals(numbers[4], 1));
    }
}

Assumption

Assumption은 특정 조건이 충족되는 경우에만 테스트를 실행하는 데 사용됩니다. 일반적으로 테스트가 제대로 실행되기 위해 필요한 외부 조건에 사용되지만 테스트 대상과 직접적인 관련은 없습니다.

package io.lcalmsky.junit;

import org.junit.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assumptions.*;

public class AssumptionTests {
    @Test
    public void trueAssumption() {
        assumeTrue(5 > 1);
//        assumeTrue(() -> 5 > 1); // 역시 람다 형태(BooleanSupplier)를 제공
        assertEquals(5 + 2, 7);
    }

    @Test
    public void falseAssumption() {
        assumeFalse(5 < 1);
//        assumeFalse(() -> 5 < 1);
        assertEquals(5 + 2, 7);
    }

    @Test
    public void assumingThatTest() {
        assumingThat(
                () -> 5 > 1, // 참이면
                () -> assertEquals(5 + 2, 7) // 실행됨
        );
//        assumingThat(
//                5 > 1,
//                () -> assertEquals(5 + 2, 7)
//        ); // assumption 부분에 바로 boolean 타입 사용 가능
    }
}

Assumption이 false일 경우 TestAbortedException이 발생하고 테스트는 건너뜁니다.

예외 테스트

기존의 Test 애노테이션 내 expected attribute 대신 사용할 수 있는 assertThrows라는 메소드를 제공합니다.

package io.lcalmsky.junit;

import org.junit.Test;

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

public class ExceptionTests {
    @Test
    public void additionalExceptionCheck() {
        // exception에 대한 추가적인 체크를 해야하는 경우
        Exception exception = assertThrows(
                NumberFormatException.class,
                () -> Integer.valueOf(null),
                "message"
        );
        // doSomething(exception);
    }

    @Test
    public void assertThrowException() {
        assertThrows(
                NumberFormatException.class,
                () -> Integer.valueOf(null)
        );
    }
}

 

다음 포스팅에서는 테스트 스위트(suites, 수트, 슈트 아니고 스위트가 맞습니다), 다이나믹 테스트에 관하여 다룰 예정입니다. 

'Test' 카테고리의 다른 글

Mock은 Stub이 아니다(Mocks Aren't Stubs)  (2) 2022.05.11
JUnit5 (2/2)  (0) 2019.11.29
댓글