본문 바로가기
Java & Kotlin/Java

JUnit5 - @TestInstance에 대해 알아보자

by devson 2021. 7. 25.

다음과 같은 JUnit 테스트 코드가 있다고 해보자. 

import org.junit.jupiter.api.Test

class TestForTestInstance {
    var number = 0

    @Test
    fun add1() {
        number++
        println("[add1] number is $number")
    }

    @Test
    fun add2() {
        number+=2
        println("[add2] number is $number")
    }
}

 

테스트를 진행하면 콘솔에 출력되는 number 값은 어떻게 될까?

테스트 순서를 정하지 않아 순서는 몰라도 결과적으로는 둘 중 하나는 number가 3으로 출력되어야 할 것 같다.

 

테스트를 실행시켜보면 다음과 같이 콘솔에 출력되는 것을 확인할 수 있다.

 

number 값이 테스트 후 롤백이라도 된 것처럼 number값이 예상한대로 출력되지 않는다.

(롤백이 된건 아니다 🤓)

 

위 상황을 이해하기 위해서는 테스트 인스턴스의 라이프 사이클을 설정하는 @TestInstance 라는 어노테이션에 대해 살펴볼 필요가 있다.

 

@TestInstance

@TestInstance의 주석을 살펴보면 다음과 같이 설명되어 있다.

TestInstance is a type-level annotation that is used to configure the lifecycle of test instances for the annotated test class or test interface.

If @TestInstance is not explicitly declared on a test class or on a test interface implemented by a test class, the lifecycle mode will implicitly default to PER_METHOD.

@TestInstance는 테스트 인스턴스의 라이프 사이클을 설정하는데 사용된다고 한다.

 

테스트 클래스에 @TestInstance가 명시적으로 지정되지 않은 경우, 기본적으로 PER_METHOD를 사용한다고 한다.

PER_METHODLifecycle enum 값으로 아래에 설명하도록 하겠다.

 

Lifecycle

Lifecycle은 @TestInstance java file 내에 선언된 enum으로 말그대로 테스트 인스턴스의 라이프 사이클을 지정하는 enum이다.

Lifecycle은 PER_METHOD, PER_CLASS가 있다.

Lifecycle.PER_METHOD

When using this mode, a new test instance will be created for each test method, test factory method, or test template method.
This mode is analogous to the behavior found in JUnit versions 1 through 4.

주석에서 볼 수 있듯, PER_METHOD 설정은 각 테스트 마다 테스트 인스턴스가 생성된다고 한다.

 

Lifecycle.PER_CLASS

When using this mode, a new test instance will be created once per test class.

PER_CLASS 설정은 테스트가 아니라 테스트 클래스 별로 테스트 인스턴스가 생성된다고 한다.

 


위에서 설명한 것 처럼 기본적으로 테스트 당 테스트 인스턴스가 생성되기 때문에,

처음 테스트 코드에 있었던 number 값은 테스트 별로 각자의 0인 number를 사용하기 때문에 3이라는 값이 나오지 않았던 것이다.

 

명확하게 확인하기 위해 다음과 같이 각 테스트에 println(this) 를 추가해보자.

import org.junit.jupiter.api.Test

class TestForTestInstance {
    var number = 0

    @Test
    fun add1() {
        number++
        println("[add1] number is $number")
        println(this)
    }

    @Test
    fun add2() {
        number+=2
        println("[add2] number is $number")
        println(this)
    }
}

 

 

위와 같이 기본(PER_METHOD)적으로 다음과 같이 테스트 시 서로 다른 테스트 인스턴스를 사용하고 있음을 확인할 수 있다.

 

PER_CLASS로 설정하고 테스트를 다시 돌려보고 차이를 확인해보자.

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestForTestInstance {
    var number = 0

    @Test
    fun add1() {
        number++
        println("[add1] number is $number")
        println(this)
    }

    @Test
    fun add2() {
        number+=2
        println("[add2] number is $number")
        println(this)
    }
}

 

 

 

결과적으로 number는 3이 되었고 각 테스트가 같은 테스트 인스턴스를 사용하고 있음을 확인할 수 있다.

 


Lifecycle.PER_CLASS 는 언제 사용할까?

테스트 인스턴스를 생성하는데 비용이 많이 드는 경우(예를 들면 테스트 용 Docker Container를 실행한다던지)에 유용하다고 한다.

 

또 테스트 끼리는 상태를 공유하지 않고 서로 격리가 되어야 좋지만, 순서대로 상태 값을 다음 테스트로 전달해야하는 경우에 유용하다고 한다.

(참고: https://www.baeldung.com/junit-testinstance-annotation#test-instance-uses)

 


 

참고:

댓글