이론/Spring

[Spring Test] Test LifeCycle

6161990 2022. 2. 27. 14:05

Today’s Study Topic

Test LifeCycle

 

Junit의 생명주기?

TestInstance

일반적으로 테스트 생명주기는 Method 를 기준으로 한다. TestInstance 생명주기가 어떻게 유지되는지 살펴보고 싶었다. 

 

우선, TestInstance의 생명주기를 Intercept하는 Extension을 작성해보았다.

Intercepting Invocations를 대충 번역한 바로는 "호출 가로채기" 였다.

  • InvocationInterceptor는 테스트 코드에 대한 호출을 가로채기 위한 API를 정의한다.
package kr.co.yeoeulsim.eatgo.domain;

import javax.transaction.Transactional;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.InvocationInterceptor;
import org.junit.jupiter.api.extension.ReflectiveInvocationContext;

import java.lang.reflect.Constructor;

@Slf4j
@Transactional
public class TestInstancePlay implements InvocationInterceptor {

    @Override
    public <T> T interceptTestClassConstructor(Invocation<T> invocation, ReflectiveInvocationContext<Constructor<T>> invocationContext, ExtensionContext extensionContext) throws Throwable {
        log.info("constructed", invocationContext.getTargetClass());
        return InvocationInterceptor.super.interceptTestClassConstructor(invocation, invocationContext, extensionContext);
    }
}

→ 클래스가 생성되는 생성자 전에 로그를 심기 위해  Junit에서 제공하는 InvocationInterceptor 인터페이스를 상속받았다.
클래스가 생성되는 지점에 대해 가로채는 클래스를 만든 셈이다.

package kr.co.yeoeulsim.eatgo.domain;

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

import java.util.concurrent.atomic.AtomicLong;

import static org.assertj.core.api.Assertions.assertThat;

@TestInstance(TestInstance.Lifecycle.PER_CLASS) // 생명주기는 클래스
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) // 순서에 의해서 테스트 실행
@ExtendWith(TestInstancePlay.class) // 커스텀 Extension Import
class TestInstancePlayTest {

    private AtomicLong count = new AtomicLong(0L);

    @BeforeEach
    public void setUp() {
        count.getAndAdd(1); // 테스트 실행 전마다 +1
    }

    @Test
    @Order(1) // 처음 실행되는 테스트
    public void test1() {
        assertThat(count).hasValue(1L);
    }

    @Test
    @Order(2) // 두번째에 실행되는 테스트
    public void test2() {
        assertThat(count).hasValue(2L); 
    }

}

테스트 코드 작성

  1. LifeCycle은 클래스 단위로 설정
  2. 공유하는 자원을 설정
  3. 테스트에 순서를 매겨서, 순서대로 실행하게 설정
  4. 내가 생각한 값과 그대로 일치하는지 확인

INFO log 확인해보자

 

→ 심어둔 로그를 확인해보면 TestInstancePlay 에서 테스트 인스턴스가 단 1번만 생성된 걸 확인할 수 있다. 

 

그러면, 

@TestInstance 을 제거 후 다시 테스트 해보자.

package kr.co.yeoeulsim.eatgo.domain;

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

import java.util.concurrent.atomic.AtomicLong;

import static org.assertj.core.api.Assertions.assertThat;

//@TestInstance(TestInstance.Lifecycle.PER_CLASS) // 생명주기는 클래스
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) // 순서에 의해서 테스트 실행
@ExtendWith(TestInstancePlay.class) // 커스텀 Extension Import
class testInstancePlayTest {

    private AtomicLong count = new AtomicLong(0L);

    @BeforeEach
    public void setUp() {
        count.getAndAdd(1); // 테스트 실행 전마다 +1
    }

    @Test
    @Order(1) // 처음 실행되는 테스트
    public void test1() {
        assertThat(count).hasValue(1L);
    }

    @Test
    @Order(2) // 두번째에 실행되는 테스트
    public void test2() {
        assertThat(count).hasValue(1L); //2L
    }

}

 

 Method 마다 LifeCycle 이 별도로 구분되어 있어서 자원을 공유하지 않게된다. 따라서 count의 기대값이 항상 1로 일치하게 된다.

→ 테스트 인스턴스가 테스트마다 생성된다는 것을 의미한다. 결과를 확인해보자.

 

 

Relevant Question

  • 이걸 가능하게 해주는 @ExtendWith은 무엇일까. 

Relevant Reference

강제 빌드 버전업