[JUnit5] JUnit5
![[JUnit5] JUnit5](/assets/img/development/server/2023-06-01/junit5_cover.png)
“해당 포스팅은 우아한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인터페이스를 가지고 있다.Platform은TestEngine을 통해서 테스트를 발견하고, 실행하며, 결과를 보고한다.JUnit Jupiter:TestEngine의 실제 구현체는 별도 모듈이며, 그 중 하나가Jupiter-Engine이다. 이 모듈은Jupiter-API를 사용해서 작성한 테스트 코드를 발견하고 실행한다.Jupiter API는JUnit5에 새롭게 추가된 테스트 코드용API로서, 개발자는Jupiter API를 사용해서 테스트 코드를 작성할 수 있다.JUnit Vintage: 기존에JUnit4버전으로 작성한 테스트 코드를 실행할 때에는Vintage-Engine모듈을 사용한다.
JUnit5 설정 방법
SpringBoot프로젝트SpringBoot2.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>
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'
}
JUnit5 @Annotations
“
JUnit Jupiter에는 테스트 구성 및 프레임워크 확장을 위해 다음과 같은 어노테이션을 지원한다. 달리 명시되지 않는 한, 모든 핵심 어노테이션은junit-jupiter-api모듈의org.junit.jupiter.api패키지에 있다.”
Annotation | description |
|---|---|
@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)으로 선언할 수 있다. 이러한 어노테이션은 상속되지 않는다. |
@Tag | Class 또는 Method 수준에서 테스트를 필터링하기 위한 태그를 선언하는 데 사용되며, TestNG 또는 JUnit4의 범주에 있는 테스트 그룹과 유사하다. 이러한 어노테이션은 클래스 수준에서는 상속되지만, 메서드 수준에서는 상속되지 않는다. |
@Disabled | Test Class 또는 Test Method를 비활성화하는 데 사용되며, JUnit4의 @Ignore와 유사하다. 이러한 어노테이션은 상속되지 않는다. |
@Timeout | @Test, @TestFactory, @TestTemplate 또는 lifecycle method의 실행이 지정된 기간을 초과하는 경우 실패하는 데 사용된다. 이러한 어노테이션은 상속된다. |
@ExtendWith | 확장을 선언적으로 등록하는 데 사용된다. 이러한 어노테이션은 상속된다. |
@RegisterExtension | 필드를 통해 확장을 프로그래밍 방식으로 등록하는 데 사용된다. 이러한 필드는 음영 처리되지 않는 한 상속된다. |
@TempDir | lifecycle method 또는 test method에서 필드 주입 또는 매개변수 주입을 통해 임시 디렉터리를 제공하는 데 사용되며, org.junit.jupiter.api.io 패키지에 있다. |
Continue with [JUnit5] JUnit5 어노테이션 ver1
Continue with [JUnit5] JUnit5 어노테이션 ver2
JUnit4 vs JUnit5 @Annotations 생명주기(LifeCycle)
Feature | JUnit4 | JUnit5 |
|---|---|---|
| Test Method임을 선언 | @Test | @Test |
해당 클래스에 위치한 모든 테스트 메서드 실행 전에 딱 한 번 실행되는 메서드. @BeforeAll 어노테이션을 사용하기 위해선 static을 선언해줘야 한다. | @BeforeClass | @BeforeAll |
해당 클래스에 위치한 모든 테스트 메서드 실행 후에 딱 한 번 실행되는 메서드 @AfterAll 어노테이션을 사용하기 위해선 static을 선언해줘야 한다. | @AfterClass | @AfterAll |
| 해당 클래스에 위치한 모든 테스트 메서드 실행 전에 실행되는 메서드 | @Before | @BeforeEach |
| 해당 클래스에 위치한 모든 테스트 메서드 실행 후에 실행되는 메서드 | @After | @AfterEach |
Test Method나 Class를 비활성화 | @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