본문 바로가기

251021 (화) JUnit 테스트 생명주기 완벽 이해하기 — @BeforeAll, @AfterAll, @BeforeEach, @AfterEach, 테스트 인스턴스

@6uiw2025. 10. 21. 13:09

 

학습목표

JUnit 테스트 생명주기 이해하기

 

 

 

@BeforeAll 메서드는 테스트 인스턴스 생성전에 실행되어야 하기 때문에 static이 붙은 게 이해가 됐는데, 왜 @AfterAll까지 static이 붙어야할까? 라는 의문지 들어서 JUnit 테스트의 생명주기에 대해 알아보게 되었다.

 

1. 예제

먼저 각 어노테이션에 따른 테스트 진행 순서를 간단히 짚어보자. 
package com.springboot.jpa;

import org.junit.jupiter.api.*;

public class TestLifeCycle {

    @BeforeAll
    static void beforeAll() {
        System.out.println("## BeforeAll Annotation 호출 ##");
    }

    @AfterAll
    static void afterAll() {
        System.out.println("## AfterAll Annotation 호출 ##");
    }

    @BeforeEach
    void beforeEach() {
        System.out.println("## BeforeEach Annotation 호출 ##");
    }

    @AfterEach
    void afterEach() {
        System.out.println("## AfterEach Annotation 호출 ##");
    }

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

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

 

2. 실행 순서(결과)

JUnit은 위 클래스를 실행할 때 다음 순서로 동작한다.

BeforeAll
BeforeEach
test1
AfterEach
BeforeEach
test2
AfterEach
AfterAll

 

즉,

  • @BeforeAll → 전체 테스트 시작 전에 한 번 실행
  • @BeforeEach → 각 테스트 실행 전에 실행
  • @AfterEach → 각 테스트 실행 후에 실행
  • @AfterAll → 모든 테스트가 끝난 뒤 한 번 실행

 

 

 

 

 


 

 3. 테스트 인스턴스란?

 

테스트 인스턴스(Test Instance)란 테스트 클래스의 객체로 new TestLifeCycle()로 생성된 하나의 객체를 의미한다.

JUnit은 기본적으로 각 테스트 메서드(@Test)마다 새로운 인스턴스를 생성해서 테스트를 실행한다.

 

 

JUnit의 테스트 실행방법

for (테스트 메서드 : @Test 메서드 목록) {
    new TestLifeCycle()                    // ① 인스턴스 생성
    실행할 객체 안에 @BeforeEach가 있나 확인 → 있다면 실행  // ② BeforeEach
    @Test 메서드 실행                                         // ③ Test
    실행할 객체 안에 @AfterEach가 있나 확인 → 있다면 실행    // ④ AfterEach
    인스턴스 폐기                                            // ⑤ 객체 제거
}

 

 

실제 실행 예시

 
--- test1 실행 시 ---
new TestLifeCycle()  ← 인스턴스 A
A.beforeEach()
A.test1()
A.afterEach()
(인스턴스 A 폐기)

--- test2 실행 시 ---
new TestLifeCycle()  ← 인스턴스 B
B.beforeEach()
B.test2()
B.afterEach()
(인스턴스 B 폐기)

 

이렇게 하면 테스트 간에 상태가 공유되지 않기 때문에 독립성이 유지된다.

 

 

 


🧬 4. 왜 @BeforeAll과 @AfterAll은 static이어야 할까?

 

JUnit은 테스트를 실행하기 전에 다음 과정을 거친다.

  1. 테스트 클래스 스캔 → 어떤 메서드에 @BeforeAll, @Test, @AfterAll이 붙었는지 확인
  2. @BeforeAll 실행
  3. 각 테스트(@Test) 실행
  4. @AfterAll 실행

문제는 2번 단계에서 아직 new TestLifeCycle()이 실행되지 않았다는 점이다.
즉, 인스턴스가 존재하지 않기 때문에 인스턴스 메서드를 호출할 수 없다.

 

그래서 클래스 레벨에서 직접 호출할 수 있도록
@BeforeAll, @AfterAll은 반드시 static이어야 한다. (테스트가 종료된 후에는 인스턴스가 삭제되므로 인스턴스 메서드를 호출할 수 없어서 @AfterAll도 static이어야 한다.)

 
@BeforeAll
static void beforeAll() { ... } //가능

@BeforeAll
void beforeAll() { ... } //인스턴스가 없으므로 불가능

 

 

 

 

 

 

 

 


5. @BeforeEach와 @AfterEach는 인스턴스 기반

 

반면 @BeforeEach, @AfterEach는
각 테스트가 실행될 때마다 생성된 인스턴스 내부에서 동작한다.

 

즉, @BeforeEach → @Test → @AfterEach는 하나의 TestLifeCycle 객체 안에서 수행된다.

 
(new TestLifeCycle)     ← 생성
beforeEach()
testX()
afterEach()
(인스턴스 삭제)

 

 

 

 

 


6. 확인용 예제

 
public class TestLifeCycle {

    private int counter = 0;

    @BeforeEach
    void beforeEach() {
        System.out.println("BeforeEach: counter=" + counter);
    }

    @Test
    void test1() {
        counter++;
        System.out.println("test1: counter=" + counter);
    }

    @Test
    void test2() {
        counter++;
        System.out.println("test2: counter=" + counter);
    }

    @AfterEach
    void afterEach() {
        System.out.println("AfterEach: counter=" + counter);
    }
}

 

출력 결과

BeforeEach: counter=0
test1: counter=1
AfterEach: counter=1

BeforeEach: counter=0
test2: counter=1
AfterEach: counter=1

 

각 테스트가 독립된 인스턴스에서 실행되기 때문에 counter 값이 항상 0에서 시작한다.

 

 

 

 


 7. PER_CLASS 모드 — 인스턴스 1개만 쓰는 방법

JUnit 5에서는 다음 설정으로 테스트 인스턴스를 하나만 만들 수 있다.

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestLifeCycle {
    @BeforeAll
    void beforeAll() { ... } // static이 아니어도 가능
}

 

이 경우에는 클래스 인스턴스가 한 번만 생성되고
모든 테스트가 같은 객체를 공유한다.

 
 
new TestLifeCycle()  ← 한 번만 생성
beforeAll()
(beforeEach → test1 → afterEach)
(beforeEach → test2 → afterEach)
afterAll()
 

이때는 @BeforeAll, @AfterAll이 static이 아니어도 된다.

 

 

 

 

 

 


 

오늘의 코멘트

처음엔 단순히 “왜 static이어야 하지?” 정도의 의문이었는데, 결국 핵심은 테스트 인스턴스의 생명주기를 이해하는 데 있었다.
@Test 메서드 하나당 새로운 인스턴스가 만들어지고 그 인스턴스 안에서 BeforeEach → Test → AfterEach가 한 싸이클로 돈다.
@BeforeAll, @AfterAll는 인스턴스가 생성되기 이전, 삭제된 이후에 실행되어야 하기 때문에 static이어야 한다.

 

'SPRING > Test' 카테고리의 다른 글

250925(목) 단위테스트  (0) 2025.09.25
6uiw
@6uiw :: LOG.INFO("MING's DEVLOG")

개발을 하면서 공부한 기록을 남깁니다

목차