[JUnit5] JUnit5

[JUnit5] JUnit5

“해당 포스팅은 우아한Tech[10분 테코톡] 🌊 바다의 JUnit5 사용법을 참고하여 포스팅하였습니다. 영상을 제작해주신 우아한Tech바다님에게 감사드립니다.”

#목차

JUnit5

단위 테스트(Unit Test) 란?

“프로그래밍에서 소스 코드의 특정 모듈이 의도된 대로 정확히 작동하는지 검증하는 절차이다.”

  • 모든 함수와 메서드에 대한 테스트 케이스(Test case)를 작성하는 절차를 말한다.
  • 이를 통해서 언제라도 코드 변경으로 인해 문제가 발생할 경우, 단시간 내에 이를 파악하고 바로 잡을 수 있도록 해준다.

JUnit 이란?

JetBrains 조사에 따르면 단위 테스트를 수행하는 Java 개발자의 93%JUnit을 사용하고, 51%Mockito 를 사용한다.”

  • Java 개발자의 93%가 사용하는 단위 테스트 프레임워크이다.

JUnit5 란?

  • JUnit5는 2017년 10월에 공개 되었으며, SpringBoot 2.2버전 이상부터 기본으로 제공하고 있다.

JUnit5 Architecture

JUnit4는 하나의 jar 파일로 의존성을 불러와 다른 라이브러리를 참조해서 사용하는 구조였는데, JUnit5부터는 JUnit5그 자체로 모듈화가 되어있다. JUnit5는 3개의 하위 프로젝트, 즉 JUnit Platform, JUnit Jupiter, JUnit Vintage로 구성이 되며 Java8부터 사용가능하다.”

  • JUnit Platform : 테스트를 발견하고 테스트 계획을 생성하는 TestEngine 인터페이스를 가지고 있다. PlatformTestEngine을 통해서 테스트를 발견하고, 실행하며, 결과를 보고한다.
  • JUnit Jupiter : TestEngine의 실제 구현체는 별도 모듈이며, 그 중 하나가 Jupiter-Engine이다. 이 모듈은 Jupiter-API를 사용해서 작성한 테스트 코드를 발견하고 실행한다. Jupiter APIJUnit5에 새롭게 추가된 테스트 코드용 API로서, 개발자는 Jupiter API를 사용해서 테스트 코드를 작성할 수 있다.
  • JUnit Vintage : 기존에 JUnit4 버전으로 작성한 테스트 코드를 실행할 때에는 Vintage-Engine 모듈을 사용한다.

JUnit5 설정 방법

  • SpringBoot 프로젝트
    • SpringBoot 2.2버전 이상부터는 기본적으로 JUnit5 의존정이 자동으로 추가되어 따로 설정할 필요 없이 바로 사용 가능하다.
// file: "build.gradle" 
// spring boot 3.0.6 버전 (2023-05-03 기준)
dependencies {
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
    useJUnitPlatform()
}
  • SpringBoot 프로젝트가 아닐 경우
    • 다음과 같이 의존성을 추가해주면 된다.

Maven Setup

<!-- file: "pom.xml"-->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.9.1</version>
    <scope>test</scope>
</dependency>

JUnit5 Platform

Gradle Setup

// file: "build.gradle"
test {
    useJUnitPlatform()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}

Using JUnit5 with Gradle

JUnit5 @Annotations

JUnit Jupiter에는 테스트 구성 및 프레임워크 확장을 위해 다음과 같은 어노테이션을 지원한다. 달리 명시되지 않는 한, 모든 핵심 어노테이션은 junit-jupiter-api 모듈의 org.junit.jupiter.api 패키지에 있다.”

Annotationdescription
@Test메서드가 테스트 메서드임을 나타낸다. JUnit4@Test 어노테이션과 달리 이 어노테이션은 attributes(속성)를 선언하지 않는데, 이는 JUnit Jupiter의 테스트 확장이 자체 전용 어노테이션을 기반으로 작동하기 때문이다. 이러한 메서드는 재정의하지 않는 한 상속된다.
@ParameterizedTest메서드가 매개변수화된 테스트임을 나타낸다. 이러한 메서드는 재정의되지 않는 한 상속된다.
@RepeatedTest메서드가 반복 테스트에 대한 테스트 템플릿임을 나타낸다. 이러한 메서드는 재정의되지 않는 한 상속된다.
@TestFactory메서드가 동적 테스트를 위한 테스트 팩토리임을 나타낸다. 이러한 메서드는 재정의되지 않는 한 상속된다.
@TestTemplate메서드가 등록된 프로바이더가 반환하는 호출 컨텍스트의 수에 따라 여러 번 호출되도록 설계된 테스트 케이스용 템플릿임을 나타낸다. 이러한 메서드는 재정의되지 않는 한 상속된다.
@TestClassOrder어노테이션된 테스트 클래스에서 @Nested 테스트 클래스에 대한 테스트 클래스 실행 순서를 구성하는 데 사용된다. 이러한 어노테이션은 상속된다.
@TestMethodOrder어노테이션된 테스트 클래스에 대한 테스트 메서드 실행 순서를 구성하는 데 사용되며, JUnit4@FixMethodOrder와 유사하다. 이러한 어노테이션은 상속된다.
@TestInstance어노테이션된 테스트 클래스에 대한 테스트 인스턴스 라이프사이클을 구성하는 데 사용된다. 이러한 어노테이션은 상속된다.
@DisplayName테스트 클래스 또는 테스트 메서드의 사용자 지정 디스플레이 이름을 선언한다. 이러한 어노테이션은 상속되지 않는다.
@DisplayNameGeneration테스트 클래스에 대한 사용자 지정 디스플레이 이름 생성기를 선언한다. 이러한 어노테이션은 상속된다.
@BeforeEach@BeforeEach 어노테이션이 달린 메서드가 현재 클래스의 각각 @Test, @RepeatedTest, @ParameterizedTest 또는 @TestFactory 어노테이션이 달린 메서드 전에 실행되어야 함을 나타낸다(JUnit4@Before와 유사). 이러한 메서드는 재정의되거나 대체되지 않는 한 상속됩니다(즉, Java의 표시 규칙에 관계없이 서명만 기준으로 대체됨).
@AfterEach@AfterEach 어노테이션이 달린 메서드가 현재 클래스의 각각 @Test, @RepeatedTest, @ParameterizedTest 또는 @TestFactory 어노테이션이 달린 메서드 이후에 실행되어야 함을 나타낸다(JUnit4@After와 유사). 이러한 메서드는 재정의되거나 대체되지 않는 한 상속된다(즉, Java의 표시 규칙에 관계없이 서명만 기준으로 대체됨).
@BeforeAll@BeforeAll 어노테이션이 달린 메서드가 현재 클래스의 모든 @Test, @RepeatedTest, @ParameterizedTest@TestFactory 어노테이션이 달린 메서드보다 먼저 실행되어야 함을 나타낸다(JUnit4@BeforeClass와 유사). 이러한 메서드는 숨겨지거나 재정의되거나 대체되지 않는 한(즉, Java의 표시 규칙에 관계없이 서명만 기준으로 대체되지 않는 한) 상속되며, per-class 테스트 인스턴스 라이프사이클이 사용되지 않는 한 정적(static)이어야 합니다.
@AfterAll@AfterAll 어노테이션이 달린 메서드가 현재 클래스의 모든 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory 어노테이션이 달린 메서드 다음에 실행되어야 함을 나타낸다(JUnit4@AfterClass와 유사). 이러한 메서드는 숨겨지거나 재정의되거나 대체되지 않는 한(즉, Java의 표시 규칙에 관계없이 서명만 기준으로 대체되지 않는 한) 상속되며, per-class 테스트 테스트 인스턴스 라이프사이클이 사용되지 않는 한 정적(static)이어야 합니다.
@Nested@Nested 어노테이션이 달린 클래스가 정적(static)이 아닌 중첩된 테스트 클래스임을 나타낸다. Java8부터 Java15까지에서는 “클래스-별” 테스트 인스턴스 라이프사이클을 사용하지 않는 한 @BeforeAll 및 @AfterAll 메서드를 @Nested 테스트 클래스에서 직접 사용할 수 없다. Java16부터는 테스트 인스턴스 라이프사이클 모드 중 하나를 사용하여 @Nested 테스트 클래스에서 @BeforeAll@AfterAll 메서드를 정적(static)으로 선언할 수 있다. 이러한 어노테이션은 상속되지 않는다.
@TagClass 또는 Method 수준에서 테스트를 필터링하기 위한 태그를 선언하는 데 사용되며, TestNG 또는 JUnit4의 범주에 있는 테스트 그룹과 유사하다. 이러한 어노테이션은 클래스 수준에서는 상속되지만, 메서드 수준에서는 상속되지 않는다.
@DisabledTest Class 또는 Test Method비활성화하는 데 사용되며, JUnit4@Ignore와 유사하다. 이러한 어노테이션은 상속되지 않는다.
@Timeout@Test, @TestFactory, @TestTemplate 또는 lifecycle method의 실행이 지정된 기간을 초과하는 경우 실패하는 데 사용된다. 이러한 어노테이션은 상속된다.
@ExtendWith확장을 선언적으로 등록하는 데 사용된다. 이러한 어노테이션은 상속된다.
@RegisterExtension필드를 통해 확장을 프로그래밍 방식으로 등록하는 데 사용된다. 이러한 필드는 음영 처리되지 않는 한 상속된다.
@TempDirlifecycle method 또는 test method에서 필드 주입 또는 매개변수 주입을 통해 임시 디렉터리를 제공하는 데 사용되며, org.junit.jupiter.api.io 패키지에 있다.

Continue with [JUnit5] JUnit5 어노테이션 ver1

Continue with [JUnit5] JUnit5 어노테이션 ver2

JUnit4 vs JUnit5 @Annotations 생명주기(LifeCycle)

FeatureJUnit4JUnit5
Test Method임을 선언@Test@Test
해당 클래스에 위치한 모든 테스트 메서드 실행 딱 한 번 실행되는 메서드. @BeforeAll 어노테이션을 사용하기 위해선 static을 선언해줘야 한다.@BeforeClass@BeforeAll
해당 클래스에 위치한 모든 테스트 메서드 실행 딱 한 번 실행되는 메서드 @AfterAll 어노테이션을 사용하기 위해선 static을 선언해줘야 한다.@AfterClass@AfterAll
해당 클래스에 위치한 모든 테스트 메서드 실행 에 실행되는 메서드@Before@BeforeEach
해당 클래스에 위치한 모든 테스트 메서드 실행 에 실행되는 메서드@After@AfterEach
Test MethodClass비활성화@Ignore@Disabled
동적 테스트에 사용N/A@TestFactory
중첩 테스트에 사용N/A@Nested
태그 지정 및 필터링@Category@Tag
확장을 선언적으로 등록할 때 사용N/A@ExtendWith

@BeforeAll vs @BeforeEach 와 @AfterAll vs @AfterEach

  • 두 개 어노테이션 모두 하나 혹은 여러 개의 테스트 조건을 setup할 때 사용하지만 하나의 테스트 클래스 안에 여러 개의 테스트가 있는 경우 @BeforeEach@AfterEach는 여러번 실행되지만, @BeforeAll@AfterAll은 딱 한 번만 실행된다.
  • @BeforeEach@AfterEach@Test 어노테이션이 붙은 메서드가 실행이 되기 전(@BeforeEach)과 후(@AfterEach) 실행이 된다.
  • @BeforeAll@AfterAll은 해당 클래스 내에 처음 시작(@BeforeAll)과 맨 마지막 끝(@AfterAll)에 실행이 된다.

JUnit5 LifeCycle 예시

import static org.assertj.core.api.Assertions.assertThat;

class JUnit5LifecycleTest {
  @BeforeAll
  static void BeforeAll() {
    System.out.println("@BeforeAll");
  }

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

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

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

  @Test
  void 라이프사이클_테스트1() {
    String str = "abc";
    System.out.println("라이프사이클_테스트1 = " + str);
    assertThat(str).contains("a");
  }

  @Test
  void 라이프사이클_테스트2() {
    String str = "abc";
    System.out.println("라이프사이클_테스트2 = " + str);
    assertThat(str).contains("b");
  }
}
결과 출력

JUnit5 LifeCycle Example 1

Assertions

  • 실제 테스트에서 검증하고자 하는 내용을 확인하는 기능을 제공하는 패키지이다.
  • 실제 값(actual)기대한 값(expected)과 같은지 확인하는 메서드이다.
  • Assertions에 대해서는 아래 포스팅에서 자세히 다루고 있습니다.

Continue with [JUnit5] JUnit5 AssertJ

Reference