어제 오늘 내일

[JUnit5] ExtensionContext.getStore()로 데이터 저장하고 가져오기 본문

IT/JUnit

[JUnit5] ExtensionContext.getStore()로 데이터 저장하고 가져오기

hi.anna 2026. 1. 26. 08:45

1. ExtensionContext.getStore()란 무엇인가

ExtensionContext.getStore()는 JUnit5 확장 모델(Extension API)에서 제공하는 강력한 저장소 기능으로,
확장(Extension) 내부에서 테스트 실행 중 데이터를 보관하고 공유할 수 있는 Key-Value 기반 스토어를 제공한다.

스토어(Store)는 단순 Map이 아니라 JUnit이 생명주기별로 관리하는 스코프 기반 저장 공간이며, 이를 활용하면 다음을 구현할 수 있다.

  • 테스트 실행 전후에 데이터를 공유
  • 타이머, 카운터 등 실행 중 누적 데이터 유지
  • 확장 간 공통 데이터 공유
  • 클래스 단위, 메서드 단위로 자동 정리되는 임시 데이터 저장

JUnit5 확장에서 가장 유용한 기능 중 하나다.

 

2. 기본 사용 예제 – 데이터 저장 및 조회

import org.junit.jupiter.api.extension.*;

public class StoreExtension implements BeforeEachCallback, AfterEachCallback {

    private static final ExtensionContext.Namespace NAMESPACE =
            ExtensionContext.Namespace.create("example", "store");

    @Override
    public void beforeEach(ExtensionContext context) {
        ExtensionContext.Store store = context.getStore(NAMESPACE);
        store.put("start", System.currentTimeMillis());
    }

    @Override
    public void afterEach(ExtensionContext context) {
        ExtensionContext.Store store = context.getStore(NAMESPACE);
        long startTime = store.get("start", long.class);
        long duration = System.currentTimeMillis() - startTime;

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

설명

  • Namespace는 스토어 구역을 구분하는 역할을 한다.
  • store.put()으로 데이터를 넣고, store.get()으로 꺼낸다.
  • 테스트 라이프사이클에 따라 자동으로 저장/정리된다.

 

3. Namespace를 사용하는 이유

Namespace는 스토어의 “폴더” 역할이다.
확장 여러 개가 존재하더라도 서로 데이터가 충돌하지 않도록 안전하게 구분하기 위해 사용한다.

private static final ExtensionContext.Namespace NAMESPACE =
        ExtensionContext.Namespace.create("my", "test", "context");

Namespace는 1개 값 또는 여러 값으로 구성할 수 있다.

 

4. 다양한 타입 저장 및 꺼내기

store.put("count", 10);
int value = store.get("count", int.class);
store.put("obj", new SomeObject());
SomeObject obj = store.get("obj", SomeObject.class);

활용 포인트

  • 숫자
  • 문자열
  • 커스텀 객체
  • 테스트 실행 중 누적 상태 등 모든 타입 저장 가능

 

5. BeforeTestExecution / AfterTestExecution에서 활용

특정 테스트 메서드 실행 시간을 측정하는 대표 패턴이다.

public class TimerExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {

    private static final Namespace NAMESPACE = Namespace.create("timer");

    @Override
    public void beforeTestExecution(ExtensionContext context) {
        context.getStore(NAMESPACE).put("start", System.nanoTime());
    }

    @Override
    public void afterTestExecution(ExtensionContext context) {
        long start = context.getStore(NAMESPACE).get("start", long.class);
        long duration = System.nanoTime() - start;

        System.out.println(context.getDisplayName() + " 실행 시간: " + duration + "ns");
    }
}

 

 

6. Store의 스코프 이해하기

스토어는 다음 스코프 중 하나에 속한다.

  • ROOT: 전체 테스트 실행 동안 유지
  • CLASS 스코프: 해당 테스트 클래스 실행 동안만 유지
  • METHOD 스코프: 각 테스트 메서드 실행 동안만 유지

예제:

context.getStore(ExtensionContext.Namespace.GLOBAL); // 글로벌 스코프

일반적으로 확장별로 독립적인 Namespace를 만들고,
JUnit이 각 테스트 라이프사이클에 따라 스토어를 자동 삭제한다.

 

7. 복수 데이터 저장을 위한 Map-like 활용

ExtensionContext.Store store = context.getStore(NAMESPACE);

store.put("userId", "A001");
store.put("status", "READY");

String userId = store.get("userId", String.class);
String status = store.get("status", String.class);

활용 사례

  • 테스트 준비 정보
  • 테스트 중간 상태
  • 외부 리소스 핸들
  • 테스트 결과 기록

 

8. 확장에서 getStore()가 유용한 상황 정리

  • 타이머, 카운터 등 실행 중 상태 값을 저장해야 할 때
  • 클래스 단위 또는 메서드 단위로 데이터를 공유해야 할 때
  • 테스트 전후 정보를 조합해 리포트 생성할 때
  • Mock 객체나 외부 리소스를 저장해 테스트 종료 후 정리해야 할 때
  • 확장 간 공통 데이터 저장소가 필요할 때

getStore()는 정교한 확장 기능을 만들기 위한 핵심 API로,
확장 기반 테스트 자동화를 구축할 때 반드시 익혀야 하는 기능이다.

 

 

반응형
Comments