어제 오늘 내일

[JUnit5] ExtensionContext로 테스트 실행 컨텍스트 다루기 본문

IT/JUnit

[JUnit5] ExtensionContext로 테스트 실행 컨텍스트 다루기

hi.anna 2026. 1. 17. 08:47

JUnit5 확장(Extension) API의 핵심은 바로 ExtensionContext입니다.
BeforeEachCallback, AfterEachCallback, ParameterResolver 등 다양한 확장 포인트에서 공통적으로 전달되는 객체이며,
"현재 어떤 테스트가 실행되고 있으며 어떤 메타데이터를 가지고 있는지"에 대한 모든 정보를 제공합니다.

이 글에서는 ExtensionContext가 제공하는 기능을 테스트 작성 관점에서 쉽게 이해할 수 있도록 정리합니다.

 

1. ExtensionContext란 무엇인가

ExtensionContext는 테스트 실행 과정에서 JUnit5가 제공하는 실행 컨텍스트 정보 객체입니다.
확장 기능 내부에서 테스트 메서드, 테스트 인스턴스, 태그, 예외 정보 등을 조회하거나 조작할 수 있도록 하는 핵심 API입니다.

ExtensionContext를 통해 접근 가능한 주요 정보는 다음과 같습니다.

  • 테스트 클래스 및 테스트 메서드 정보
  • DisplayName, 태그 등 메타데이터
  • 테스트 인스턴스(this)
  • 실행 중 발생한 예외
  • Store(컨텍스트 스코프 저장소)
  • 부모 컨텍스트(Test → Class → Engine 레벨)

확장 기능을 제대로 활용하려면 ExtensionContext 구조를 이해하는 것이 필수입니다.

 

2. 테스트 메타데이터 조회하기

ExtensionContext는 현재 실행되는 테스트의 메타데이터를 쉽게 조회할 수 있도록 다양한 메서드를 제공합니다.

예제

@Override
public void beforeEach(ExtensionContext context) {
    String className = context.getRequiredTestClass().getSimpleName();
    String methodName = context.getRequiredTestMethod().getName();
    String displayName = context.getDisplayName();

    System.out.println("테스트 클래스: " + className);
    System.out.println("테스트 메서드: " + methodName);
    System.out.println("표시 이름: " + displayName);
}

설명

  • getRequiredTestClass() / getRequiredTestMethod()는 Optional 없이 즉시 값 반환
  • getDisplayName()은 @DisplayName이 지정되면 해당 값이 반환

확장 기반의 테스트 로깅에서 매우 유용합니다.

 

3. 테스트 인스턴스 접근하기

ExtensionContext는 테스트 객체(this)에 접근할 수 있게 해줍니다.

예제

@Override
public void beforeEach(ExtensionContext context) {
    Object testInstance = context.getRequiredTestInstance();
    System.out.println("테스트 인스턴스: " + testInstance.getClass().getSimpleName());
}

활용 예

  • 필드 초기화 검증
  • @Mock 객체 자동 주입
  • 커스텀 라이프사이클 관리

 

4. 테스트 실행 예외 확인하기

AfterEachCallback에서 테스트 예외 정보를 조회할 수 있습니다.

예제

@Override
public void afterEach(ExtensionContext context) {
    context.getExecutionException().ifPresent(ex -> {
        System.out.println("테스트에서 예외 발생: " + ex.getMessage());
    });
}

설명

  • 실패/에러 여부에 따라 예외를 기록하거나 추가 처리 가능

 

5. Store를 활용한 확장 스코프 데이터 저장

ExtensionContext의 가장 강력한 기능 중 하나는 Store입니다.
테스트 클래스 단위, 메서드 단위 등 다양한 스코프에 데이터를 저장할 수 있습니다.

예제

@Override
public void beforeEach(ExtensionContext context) {
    ExtensionContext.Store store =
            context.getStore(ExtensionContext.Namespace.create("my", "custom"));

    store.put("startTime", System.currentTimeMillis());
}

@Override
public void afterEach(ExtensionContext context) {
    ExtensionContext.Store store =
            context.getStore(ExtensionContext.Namespace.create("my", "custom"));

    long startTime = store.get("startTime", Long.class);
    long duration = System.currentTimeMillis() - startTime;

    System.out.println("테스트 실행 시간: " + duration + "ms");
}

설명

  • Namespace를 통해 확장 간 데이터 충돌을 방지
  • beforeEach/afterEach 사이에서 상태를 공유할 때 유용

 

6. 컨텍스트 계층 구조 이해하기

ExtensionContext는 계층 구조로 되어 있으며 다음 단계가 존재합니다.

  1. Engine 레벨(JUnit 플랫폼 전체)
  2. Test Class 레벨
  3. Test Method 레벨

특징

  • 부모 컨텍스트로 이동하여 상위 레벨 정보를 확인할 수 있음
  • Store도 레벨별로 분리되어 필요에 따라 데이터 공유 범위 조절 가능

예제: 상위 클래스 정보 가져오기

ExtensionContext parent = context.getParent().orElse(null);

 

7. 확장 기능에서 ExtensionContext 활용 사례

다음과 같은 상황에서 ExtensionContext는 핵심 도구가 됩니다.

  1. 테스트 실행 로깅
    • 테스트 이름, 태그, 표시 이름 등을 자동 수집
  2. 테스트 결과 분석
    • 실패 예외 기록, 테스트 성공 여부 체크
  3. 리소스 관리 확장
    • 테스트 시작/종료 시 리소스 자동 확보/해제
  4. 커스텀 테스트 라이프사이클 구현
    • BeforeEach/AfterEach 동작을 확장 기반으로 관리
  5. 테스트 간 상태 공유 or 제한
    • Store를 이용해 테스트 전체 또는 개별 메서드 단위 데이터 저장

 

8. 사용 시 주의할 점

  1. Store를 남용하면 테스트 간 결합도가 높아질 수 있음
  2. 테스트 인스턴스에 직접 상태를 주입할 때는 사이드 이펙트 발생 가능
  3. 필요 이상으로 많은 메타데이터 조회는 성능에 영향을 줄 수 있음
  4. 확장은 전역적으로 적용되면 모든 테스트에 영향을 주므로 범위 설정 필요

 

9. 정리

  • ExtensionContext는 JUnit5 확장 기능의 중심 API
  • 테스트 메타데이터 조회, 테스트 인스턴스 접근, 예외 확인 등 풍부한 정보를 제공
  • Store를 이용하면 라이프사이클 전후로 안전하게 상태 저장 가능
  • 확장 기반 로깅, 리소스 관리, 정책 기반 테스트 제어 등에서 필수적으로 사용됨

 

 

반응형
Comments