Spring

Junit에서 TestInstance의 생명주기

Tommy__Kim 2023. 7. 3. 10:54

Spring을 사용할 때 보통은 Junit을 사용해서 테스트를 실행하게 됩니다.

오늘은 테스트 코드 실행 시 어떠한 생명주기를 가지며 테스트를 실행하는지 알아보도록 하겠습니다.

Test LifeCycle Of Junit

@BeforeAll

BeforeAll 어노테이션은 클래스에 정의된 모든 테스트를 시작하기 전에 실행하는 메서드입니다.
BeforeAll 어노테이션이 붙은 메서드는 static 접근 제어자를 사용해야 합니다.

@BeforeEach

각각의 테스트를 진행하기 전에 실행하는 메서드 입니다.
BeforeEach 어노테이션이 붙은 메서드는 접근 제어자 static를 사용할 수 없습니다.

@AfterEach

각각의 테스트가 끝난 후 실행하는 메서드입니다.
Aftereach 어노테이션이 붙은 메서드는 접근 제어자 static를 사용할 수 없습니다.

@AfterAll

모든 테스트가 종료된 후 실행하는 메서드입니다.
AfterAll 어노테이션이 붙은 메서드는 static 접근 제어자를 사용해야 합니다.

public class TestInstanceLifeCycle {

    @BeforeAll
    static void beforeAll() {
        System.out.println("BeforeAll 실행");
        System.out.println("==============");
    }

    @BeforeEach
    void beforeEach() {
        System.out.println("BeforeEach 실행");
        System.out.println("==============");
    }

    @AfterEach
    void afterEach() {
        System.out.println("==============");
        System.out.println("AfterEach 실행");
        System.out.println("==============");
    }
    @AfterAll
    static void afterAll() {
        System.out.println("AfterAll 실행");
    }



    @Test
    void test1() {
        System.out.println("test1 실행");
    }

    @Test
    void test2() {
        System.out.println("test2 실행");
    }

}

다음과 같은 테스트 코드가 있을 때 실행결과는 다음과 같습니다.

BeforeAll 실행
==============
BeforeEach 실행
==============
test1 실행
==============
AfterEach 실행
==============
BeforeEach 실행
==============
test2 실행
==============
AfterEach 실행
==============
AfterAll 실행

Test Instance

테스트 메서드는 테스트 클래스의 인스턴스를 각각 생성합니다.

public class LifeCycleOfTest {
    private int sum = 1;    // 테스트 메서드를 실행하기 전에 테스트 클래스의 새 인스턴스를 만든다.

    @Test
    void addingTwo() {
        this.sum += 2;
        assertThat(this.sum).isEqualTo(3);
    }

    @Test
    void addingThree() {
        this.sum += 3;
        assertThat(this.sum).isEqualTo(4);
    }

}

다음과 같은 테스트 코드가 있을 때 테스트를 실행할 때마다 sum을 재할당 합니다.
그렇기 때문에 두 가지의 테스트코드는 성공합니다.

@TestInstance

TestInstance 어노테이션은 테스트 인스턴스의 생명주기를 클래스 단위로 가져가게끔 설정할 수 있습니다.
@TestInstance(TestInstance.Lifecycle.PER_CLASS)를 테스트 코드에 할당해 주면 됩니다.

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class LifeCycleOfTest {
    private int sum = 1;    // 인스턴스의 생명주기 => 클래스
    private Person person = new Person("Tommy"); // 인스턴스의 생명주기 => 클래스

    @Test
    @Order(1)
    void addingTwo() {
        System.out.println("hashCode of person = " + person.hashCode());
        this.sum += 2;
        assertThat(this.sum).isEqualTo(3);
    }

    @Test
    @Order(2)
    void addingThree() {
        System.out.println("hashCode of person = " + person.hashCode());
        this.sum += 3;
        assertThat(this.sum).isEqualTo(6);
    }

}

해당 예제에서 인스턴스는 primitive 타입인 sum, 객체 타입인 person이 있습니다.
테스트 인스턴스의 생명주기가 클래스 단위로 가져가기 때문에 sum의 상태 및 person 객체의 상태가 유지됩니다.
그렇기 때문에 sum의 값 검증을 했을 때 테스트 코드가 성공하는 것을 알 수 있습니다.
person의 경우에도 새로 생성하지 않고 기존에 만든 인스턴스를 사용하기 때문에 hashCode가 동일함을 알 수 있습니다.

hashCode of person = 540325452
hashCode of person = 540325452

@MethodOrderer

테스트 코드는 기본적으로 테스트의 순서를 지정할 수 없습니다. 하지만 어떠한 경우에는 순차적인 테스트를 해야 할 때가 있습니다.
MethodOrderer 어노테이션을 사용한다면 테스트의 순서를 지정할 수 있습니다.

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class LifeCycleOfTest {
    private int sum = 1;    // 인스턴스의 생명주기 => 클래스
    private Person person = new Person("Tommy"); // 인스턴스의 생명주기 => 클래스

    @Test
    @Order(1)
    void addingTwo() {
        System.out.println("hashCode of person = " + person.hashCode());
        this.sum += 2;
        assertThat(this.sum).isEqualTo(3);
    }

    @Test
    @Order(2)
    void addingThree() {
        System.out.println("hashCode of person = " + person.hashCode());
        this.sum += 3;
        assertThat(this.sum).isEqualTo(6);
    }

}

앞서 살펴본 예제와 동일한 예제입니다.
이 테스트의 경우 인스턴스의 상태가 유지된다는 가정하에 테스트를 진행했습니다.
만약 테스트가 addingThree() -> addingTwo()가 실행된다면 assertThat에서 원하는 결과가 다르기 때문에 테스트 결과가 실패하게 됩니다.
이러한 경우에 테스트의 실행 순서를 강제해야 합니다.

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)

Order 어노테이션을 사용해 테스트 순서를 진행하겠다. 명시해 줍니다.

그 후 제가 의도하는 순서에 따라 @Order()를 사용해서 순서를 지정해 주면 됩니다.