어제 오늘 내일

[JUnit] @BeforeEach, @AfterEach로 테스트 공통 준비 코드 관리하기 본문

IT/JUnit

[JUnit] @BeforeEach, @AfterEach로 테스트 공통 준비 코드 관리하기

hi.anna 2026. 1. 8. 01:04

1. 왜 공통 준비 코드가 필요합니까?

테스트를 여러 개 작성하다 보면,
매번 같은 객체를 생성하거나 초기 상태를 셋업하는 코드가 반복되는 경우가 많습니다.
예를 들어, 매 테스트마다 new Service(), new Repository()를 만들거나 더미 데이터를 넣는 코드가 계속 등장합니다.

이런 코드를 그대로 두면

  • 테스트 코드가 길어지고
  • 수정할 때 중복된 부분을 전부 고쳐야 하며
  • 각 테스트의 핵심 의도가 잘 보이지 않습니다.

JUnit의 @BeforeEach, @AfterEach를 사용하면 이런 공통 준비 코드와 정리 코드를 한 곳에 모아서 관리할 수 있습니다.

 

2. @BeforeEach – 각 테스트 전에 실행되는 준비 메소드

@BeforeEach는 말 그대로 각 테스트 메소드가 실행되기 “직전”에 항상 먼저 실행되는 메소드에 붙이는 애너테이션입니다.
공통 객체 생성, 초기 상태 세팅, 공통 테스트 데이터 준비 등에 사용합니다.

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class BankAccountTest {

    private BankAccount account;

    @BeforeEach
    void setUp() {
        // 각 테스트 전에 항상 새 계좌를 만들고 잔액을 1000으로 맞춥니다.
        account = new BankAccount(1000);
    }

    @Test
    void 입금하면_잔액이_증가합니다() {
        account.deposit(500);
        // 잔액이 1500인지 검증합니다.
        org.junit.jupiter.api.Assertions.assertEquals(1500, account.getBalance());
    }

    @Test
    void 출금하면_잔액이_감소합니다() {
        account.withdraw(300);
        org.junit.jupiter.api.Assertions.assertEquals(700, account.getBalance());
    }
}

설명

  • setUp() 메소드는 @BeforeEach 덕분에 각 테스트 메소드 실행 전에 매번 호출됩니다.
  • 그래서 입금 테스트와 출금 테스트 모두 초기 잔액이 1000인 새 계좌를 가지고 시작합니다.
  • 공통 준비 코드를 위로 올려서, 각 테스트 메소드는 행위(입금, 출금)와 결과 검증만 집중할 수 있습니다.

 

3. @AfterEach – 각 테스트 후에 실행되는 정리 메소드

@AfterEach는 각 테스트 메소드가 끝난 뒤에 실행되는 메소드에 붙입니다.
파일 닫기, DB 연결 종료, 임시 데이터 삭제 등 리소스를 정리하는 코드를 넣을 때 유용합니다.

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class FileWriterTest {

    private TestFileWriter writer;

    @BeforeEach
    void setUp() {
        writer = new TestFileWriter("test-output.txt");
    }

    @AfterEach
    void tearDown() {
        // 각 테스트가 끝날 때마다 파일 리소스를 정리합니다.
        writer.close();
        writer.deleteFile();
    }

    @Test
    void 파일에_문자열을_쓰기_테스트() {
        writer.write("Hello");
        org.junit.jupiter.api.Assertions.assertTrue(writer.exists());
    }
}

설명

  • setUp()에서 테스트에 사용할 TestFileWriter를 준비합니다.
  • tearDown()에서 파일을 닫고 삭제하여, 테스트 간에 파일이 남아서 영향을 주지 않도록 합니다.
  • 이렇게 하면 테스트가 여러 번 실행되거나 실패하더라도 리소스가 깔끔하게 정리됩니다.

 

4. 공통 준비 코드가 없을 때와 비교하기

공통 준비 코드를 쓰지 않으면 다음과 같은 코드가 되기 쉽습니다.

@Test
void 입금_테스트() {
    BankAccount account = new BankAccount(1000);
    account.deposit(500);
    org.junit.jupiter.api.Assertions.assertEquals(1500, account.getBalance());
}

@Test
void 출금_테스트() {
    BankAccount account = new BankAccount(1000);
    account.withdraw(300);
    org.junit.jupiter.api.Assertions.assertEquals(700, account.getBalance());
}

이 경우

  • new BankAccount(1000); 코드가 반복되고
  • 초기 상태를 바꾸고 싶을 때 모든 테스트 메소드를 수정해야 합니다.

@BeforeEach를 사용하면 한 줄만 고치면 전체 테스트에 반영되므로 유지보수가 훨씬 쉬워집니다.

 

5. @BeforeEach, @AfterEach 사용 시 팁

  1. 테스트마다 독립적인 상태를 유지합니다.
    @BeforeEach에서 새 객체를 만들면 각 테스트가 서로 영향을 주지 않습니다.
  2. 비즈니스 로직은 넣지 않습니다.
    이 메소드들은 준비와 정리만 담당하도록 두고, 실제 로직 검증은 테스트 메소드에서 수행하는 것이 좋습니다.
  3. 공통 초기화가 너무 복잡하다면 헬퍼 메소드로 분리합니다.
    setUp() 안에서 다시 작은 메소드로 쪼개면 가독성이 좋아집니다.
  4. 무거운 리소스는 @BeforeAll / @AfterAll도 고려합니다.
    DB 컨테이너 같이 한 번만 띄우고 전체 테스트에서 공유해도 되는 경우에는 클래스 단위 애너테이션을 함께 생각할 수 있습니다.

 

 

반응형
Comments