Java & Spring / / 2023. 5. 22. 11:41

[Java Test] 1. JUnit5 (4) properties, 확장, 마이그레이션

728x90
 

 


 

Contents

     

     

     

    1. junit-platform.properties

    JUnit 설정 파일을 별개로 클래스패스 루트 (src/test/resources/)에 넣어서, 일괄적용을 시킬 수 있다.

    #테스트 이름 표기 전략 설정
    junit.jupiter.displayname.generator.default = \
        org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores
    
    #테스트 인스턴스 라이프사이클 설정
    junit.jupiter.testinstance.lifecycle.default = per_class
    
    # 확장팩 자동 감지 기능
    # junit5는 확장 기능이 많이 바뀌었는데, 자동감지할 수 있도록 하는 방식이다.
    junit.jupiter.extensions.autodetection.enabled = true
    
    # @Disabled 무시하고 실행하기 - 모든 테스트 실행
    junit.jupiter.conditions.deactivate = org.junit.*DisabledCondition

     

    2. 확장 모델 (Extension)

    - JUnit 4 확장 모델 : @RunWith(Runner), TestRule, MethodRule.
    - JUnit 5 확장 모델 : Extension으로 통일(단순)

    기존 4에서는 방식이 다양했는데, 5에서는 Extension으로 사용되고 기존 방식은 쓸 수 없다.

     

    확장팩 등록 방법

    • 선언적인 등록  : @ExtendWith
    • 프로그래밍 등록  : @RegisterExtension
    • 자동 등록 자바 : ServiceLoader 이용

     

    선언적인 등록 (클래스)  : @ExtendWith

    @Test
    @Target(ElementType.METHOD) // 메소드에 사용
    @Retention(RetentionPolicy.RUNTIME) // 어노테이션 정보가 런타임까지 유지
    public @interface SlowTest {
    }
    

    2개의 라이크사이클 콜백을 구현한다.

    테스트하는데 걸리는 시간을 측정

    ExtensionContext를 파라미터로 받는데, 여기에 값을 저장하는 Store 인터페이스가 있다.

     

    Name정보를 만드는데, Context에서 클래스와 메소드 네임에서 빼온다.

     

    before에서 시작 시간을 넣고, after에서 넣어둔 시작 시간을 가져와서 총 소요시간을 계산한다.

    만약 1초 이상 시간이

    public class FindSlowTestExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
    
        private static final long THRESHOLD = 1000L; // 1초
        private static final String START_TIME = "START_TIME";
    
        @Override
        public void beforeTestExecution(ExtensionContext context) throws Exception {
            ExtensionContext.Store store = getStore(context);
            store.put(START_TIME, System.currentTimeMillis());
        }
    
        @Override
        public void afterTestExecution(ExtensionContext context) throws Exception {
    
            // SlowTest 어노테이션 여부 확인
            Method requiredTest = context.getRequiredTestMethod();
            SlowTest annotation = requiredTest.getAnnotation(SlowTest.class);
    
            String testMethodName = context.getRequiredTestMethod().getName();
            ExtensionContext.Store store = getStore(context);
            long start_time = store.remove(START_TIME, long.class);
            long duration = System.currentTimeMillis() - start_time;    // 소요시간 측정
            if (duration > THRESHOLD && annotation == null){  // 1초 이상 되면, 안내 메시지를 띄움
                System.out.printf("Please consider mark method [%s] with @SlowTest.\n", testMethodName);
            }
        }
    
        private ExtensionContext.Store getStore(ExtensionContext context){
            String testClassName = context.getRequiredTestClass().getName(); // 클래스명 추출
            String testMethodName = context.getRequiredTestMethod().getName(); // 클래스명 추출
            return context.getStore(ExtensionContext.Namespace.create(testClassName, testMethodName));
        }
    @ExtendWith(FindSlowTestExtension.class) // 1. 선언적인 방법
    public class ExtensionTest {
    
        @Test
        void slowTest() throws InterruptedException {
            Thread.sleep(1000);
            System.out.println("Slow Method for extension test");
        }
    
    }

    @SlowTest
    void slowTest() throws InterruptedException {
        Thread.sleep(1000);
        System.out.println("Slow Method for extension test");
    }

     

    프로그래밍 등록 (메소드)  : @RegisterExtension

    선언적으로 클래스에 @ExtendWith를 적용할 경우, 생성자를 만들어도 매번 실행 시간을 다르게 조작하기가 어렵다.

    public class FindSlowTestExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
    
        // private static final long THRESHOLD = 1000L; // 1초
        private long THRESHOLD = 1000L;
        private static final String START_TIME = "START_TIME";
    
        // @RegisterExtension 사용을 위한 생성자
        public FindSlowTestExtension(long threshold){
            this.THRESHOLD = threshold;
        }
        
        
        // ....

    이럴 때는 메소드에 @RegisterExtension을 붙여서 쓴다.

    //@ExtendWith(FindSlowTestExtension.class) // 1. 선언적인 방법
    public class ExtensionTest {
    
        @RegisterExtension  // 2. 프로그래밍적인 방법
        static FindSlowTestExtension findSlowTestExtension = new FindSlowTestExtension(500L);
    
        @Test
        void slowTest() throws InterruptedException {
            Thread.sleep(1000);
            System.out.println("Slow Method for extension test");
        }
    }

     

    자동 등록 자바 : ServiceLoader 이용

    서비스로더를 이용해서 확장팩을 자동등록 시키려면, junit-platform.properties의 설정을 true로 해준다.

    # 확장팩 자동 감지 기능
    junit.jupiter.extensions.autodetection.enabled = true

    https://junit.org/junit5/docs/current/user-guide/#extensions-registration-automatic

    원하지 않은 extension을 사용하게 되기 때문에, 명시적으로 해주는 쪽이 좋다.

     

    확장팩 만드는 방법

    • 테스트 실행 조건
    • 테스트 인스턴스 팩토리
    • 테스트 인스턴스 후-처리기
    • 테스트 매개변수 리졸버
    • 테스트 라이프사이클 콜백
    • 예외 처리
    • ...
    •  

    참고https://junit.org/junit5/docs/current/user-guide/#extensions 

     

    JUnit 5 User Guide

    Although the JUnit Jupiter programming model and extension model do not support JUnit 4 features such as Rules and Runners natively, it is not expected that source code maintainers will need to update all of their existing tests, test extensions, and custo

    junit.org

     

     

     

    5. JUnit4 -> 5마이그레이션

    junit-vintage-engine 의존성을 추가하면 JUnit3,4 테스트를 실행할 수 있다.

     

    Vintage engine

    2.2버젼 이하 스프링부트 프로젝트를 만들면 vintage-engine이 빠져있으므로 이를 추가해준다.

     

    - Maven  : exclusion 부분을 제외

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    <!--     <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
     -->
    </dependency>

    - Gradle : exclude 제외

    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        // exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }

     

    사실 스프링 부트 2.2부터는 JUnit5가 자동 탑재되기 때문에 이 부분을 쓸 필요는 거의 없다.

    만약 마이그레이션을 해서 Vintage 엔진을 추가해주면, 태그에 따라 사용하는 라이브러리가 다르다.

     

    - Junit4 > junit

    import org.junit.Test;

    - Junit5 > junit jupiter api

    import org.junit.jupiter.api.Test;

     

    Rule 지원x

    @Rule을 지원하지 않지만 junit-jupiter-migrationsupport 모듈이 제공하는 @EnableRuleMigrationSupport를 사용할 수 있다. 다만 아래 3개만 지원하므로 완벽하게 지원하지는 않는다.

    • ExternalResource
    • Verifier
    • ExpectedException

     

    왼쪽 태그들이 JUnit5에서는 오른쪽으로 바뀌었다.

    @Category(Class)  ->  @Tag(String)
    @RunWith, @Rule, @ClassRule  -> @ExtendWith, @RegisterExtension
    @Ignore -> @Disabled
    @Before, @After, @BeforeClass, @AfterClass -> @BeforeEach, @AfterEach, @BeforeAll, @AfterAll

     

    마이그레이션 순서

    1. @RunWIth 제거 : @SpringBootTest에 이미 @ExtendWith가 붙어있다.

    2. @Ignore 제거 : 클래스 무시 -> @Disabled는 메소드에도 사용 가능

    3. @Before -> @BeforeEach

    4. public 상관 x (놔둬도 됨

    5. @Test -> Jupiter.api로 변경

    6. Description 용 -> @DisplayName 변경, 추가

     

     

    출처 : '더 자바, 애플리케이션을 테스트하는 다양한 방법'(백기선), 자료&13~15강

    300x250
    • 네이버 블로그 공유
    • 네이버 밴드 공유
    • 페이스북 공유
    • 카카오스토리 공유