JUnit 5 애노테이션

JUnit 5 애노테이션

테스트를 작성하다 보면 "이 메서드에 @Test만 붙이면 되는 거지?" 하는 순간이 한 번쯤 있죠. 그런데 JUnit Jupiter에서 테스트를 구성하고 확장하려면 딱 하나의 애노테이션이 아니라 여러 애노테이션을 역할에 맞게 조합해서 쓰게 돼요. 이 페이지에서는 JUnit Jupiter가 기본으로 제공하는 애노테이션을 하나씩 짚어볼게요.

출처: JUnit 5 User Guide — Annotations

본문

JUnit Jupiter는 테스트를 구성하고 프레임워크를 확장하기 위한 애노테이션을 지원해요. 특별한 언급이 없는 한, 모든 핵심 애노테이션은 junit-jupiter-api 모듈의 org.junit.jupiter.api 패키지에 들어 있어요.

이제 각 애노테이션이 무엇을 뜻하는지 살펴볼게요.

  • @Test — 메서드가 테스트 메서드임을 나타내요. JUnit 4의 @Test와 달리 속성을 선언하지 않아요. JUnit Jupiter의 테스트 확장은 각자 전용 애노테이션으로 동작하기 때문이죠. 오버라이드되지 않는 한 이런 메서드는 상속돼요.
  • @ParameterizedTest — 메서드가 파라미터화 테스트임을 나타내요. 오버라이드되지 않는 한 상속돼요.
  • @RepeatedTest — 메서드가 반복 테스트를 위한 테스트 템플릿임을 나타내요. 오버라이드되지 않는 한 상속돼요.
  • @TestFactory — 메서드가 동적 테스트를 위한 테스트 팩토리임을 나타내요. 오버라이드되지 않는 한 상속돼요.
  • @TestTemplate — 메서드가 등록된 provider가 돌려준 호출 컨텍스트 개수만큼 여러 번 호출되도록 설계된 테스트 케이스의 템플릿임을 나타내요. 오버라이드되지 않는 한 상속돼요.
  • @TestClassOrder — 애노테이션이 붙은 테스트 클래스에서 @Nested 테스트 클래스의 실행 순서를 구성할 때 써요. 이런 애노테이션은 상속돼요.
  • @TestMethodOrder — 애노테이션이 붙은 테스트 클래스의 테스트 메서드 실행 순서를 구성할 때 써요. JUnit 4의 @FixMethodOrder와 비슷하죠. 성격상 상속돼요.
  • @TestInstance — 애노테이션이 붙은 테스트 클래스의 테스트 인스턴스 라이프사이클을 구성해요. 상속돼요.
  • @DisplayName — 테스트 클래스나 테스트 메서드에 사용자 지정 표시 이름을 선언해요. 상속되지는 않아요.
  • @DisplayNameGeneration — 테스트 클래스에 사용자 지정 표시 이름 생성기를 선언해요. 상속돼요.
  • @BeforeEach — 현재 클래스의 각 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory 메서드 실행 전에 실행해야 함을 나타내요. JUnit 4의 @Before에 해당하죠. 오버라이드되지 않는 한 상속돼요.
  • @AfterEach — 현재 클래스의 각 테스트 메서드 실행 후에 실행되어야 함을 나타내요. JUnit 4의 @After에 해당해요. 오버라이드되지 않는 한 상속돼요.
  • @BeforeAll — 현재 최상위 또는 @Nested 테스트 클래스의 모든 테스트 메서드 실행 전에 실행되어야 함을 나타내요. JUnit 4의 @BeforeClass에 해당하고, "per-class" 테스트 인스턴스 라이프사이클을 쓰지 않는 한 static이어야 해요. 오버라이드되지 않는 한 상속돼요.
  • @AfterAll — 현재 최상위 또는 @Nested 테스트 클래스의 모든 테스트 메서드 실행 후에 실행되어야 함을 나타내요. JUnit 4의 @AfterClass에 해당하고, "per-class" 라이프사이클을 쓰지 않는 한 static이어야 해요. 오버라이드되지 않는 한 상속돼요.
  • @ParameterizedClass — 클래스가 파라미터화 클래스임을 나타내요. 상속돼요.
  • @BeforeParameterizedClassInvocation — 파라미터화 클래스의 각 호출 전에 한 번 실행되어야 하는 메서드를 나타내요. 오버라이드되지 않는 한 상속돼요.
  • @AfterParameterizedClassInvocation — 파라미터화 클래스의 각 호출 후에 한 번 실행되어야 하는 메서드를 나타내요. 오버라이드되지 않는 한 상속돼요.
  • @ClassTemplate — 클래스가 등록된 provider가 돌려준 호출 컨텍스트 개수만큼 여러 번 실행되도록 설계된 테스트 클래스의 템플릿임을 나타내요. 상속돼요.
  • @Nested — 클래스가 비정적 중첩 테스트 클래스임을 나타내요. 상속되지는 않아요.
  • @Tag — 클래스 또는 메서드 수준에서 테스트 필터링용 태그를 선언할 때 써요. TestNG의 테스트 그룹이나 JUnit 4의 Categories와 비슷하죠. 클래스 수준에서는 상속되고 메서드 수준에서는 상속되지 않아요.
  • @Disabled — 테스트 클래스나 테스트 메서드를 비활성화할 때 써요. JUnit 4의 @Ignore와 비슷하고, 상속되지는 않아요.
  • @AutoClose — 필드가 테스트 실행 후 자동으로 닫힐 리소스임을 나타내요. 필드는 상속돼요.
  • @Timeout — 테스트, 테스트 팩토리, 테스트 템플릿 또는 라이프사이클 메서드가 정해진 시간을 초과하면 실패 처리할 때 써요. 상속돼요.
  • @TempDir — 테스트 클래스 생성자, 라이프사이클 메서드 또는 테스트 메서드의 필드 주입이나 파라미터 주입으로 임시 디렉터리를 공급할 때 써요. org.junit.jupiter.api.io 패키지에 있고, 필드는 상속돼요.
  • @ExtendWith — 확장을 선언적으로 등록할 때 써요. 상속돼요.
  • @RegisterExtension — 필드를 통해 확장을 프로그래밍 방식으로 등록할 때 써요. 필드는 상속돼요.

일부 애노테이션은 아직 실험적일 수 있어요. 자세한 내용은 Experimental APIs 표를 참고하면 돼요.

메타 애노테이션과 합성 애노테이션

JUnit Jupiter 애노테이션은 메타 애노테이션으로 쓸 수 있어요. 즉, 우리가 직접 만든 합성 애노테이션을 정의하면 그 메타 애노테이션의 의미를 자동으로 물려받는다는 뜻이죠.

예를 들어 코드베이스 곳곳에서 @Tag("fast")를 복사·붙여넣기 대신, @Fast라는 사용자 정의 합성 애노테이션을 만들어 쓸 수 있어요. @Fast@Tag("fast")를 대체하는 드롭인으로 동작하죠.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.junit.jupiter.api.Tag;

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Tag("fast")
public @interface Fast {
}

아래는 @Fast 애노테이션을 사용하는 @Test 메서드예요.

@Fast
@Test
void myFastTest() {
    // ...
}

한 발 더 나아가 @Tag("fast")@Test를 동시에 대체하는 @FastTest 애노테이션을 도입할 수도 있어요.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Tag("fast")
@Test
public @interface FastTest {
}

JUnit은 다음 메서드를 "fast" 태그가 붙은 @Test 메서드로 자동으로 인식해요.

@FastTest
void myFastTest() {
    // ...
}

더 알아보기