JUnit 4 작동 원리 및 실행 순서

Junit - 자바 단위테스트 도구

1. JUnit4란 무엇인가요?

JUnit(제이유닛)은 자바(Java) 프로그래밍 언어용 단위 테스트(Unit Test) 프레임워크입니다. 우리가 자동차를 만들 때, 자동차 전체를 조립하기 전에 '바퀴가 잘 굴러가는지', '엔진이 잘 돌아가는지' 부품별로 먼저 확인하죠? 프로그래밍에서도 마찬가지로 프로그램의 가장 작은 단위(주로 메서드)가 제대로 동작하는지 확인하는 것을 '단위 테스트'라고 합니다.

 

JUnit4는 이 단위 테스트를 아주 쉽고 규칙적으로 작성하고 실행할 수 있게 도와주는 도구입니다. 현재는 JUnit5가 최신 버전이지만, 실무의 수많은 기존 프로젝트들이 여전히 JUnit4를 사용하고 있어 알아두면 매우 유용합니다.

 

2. 작동 원리: 어떻게 내 코드를 알아서 테스트할까?

JUnit의 핵심 작동 원리는 '어노테이션(Annotation)' '리플렉션(Reflection)'에 있습니다.

  • 어노테이션 (@) : 코드에 다는 '명찰'이라고 생각하시면 됩니다. 메서드 위에 @Test라고 명찰을 달아두면, JUnit은 "아, 이 메서드는 테스트용이구나!"라고 인식합니다.
  • 리플렉션 : 자바의 고급 기능 중 하나로, 프로그램이 실행될 때 자신의 구조(클래스, 메서드, 어노테이션 등)를 들여다볼 수 있는 기능입니다.

요약하자면 여러분이 실행 버튼을 누르면, JUnit 프레임워크가 여러분의 코드를 쭉 훑어보면서 @Test 명찰이 붙은 메서드들만 쏙쏙 골라내어 자동으로 실행하고, 결과가 맞는지 틀린지(성공/실패)를 판가름해 줍니다.

 

3. 실행 순서 (라이프사이클)

테스트는 어떤 흐름으로 진행될까요?  JUnit4에서 가장 중요한 부분입니다. 테스트를 실행할 때마다 특정한 순서대로 메서드들이 호출됩니다. 아래 5가지 주요 어노테이션의 역할을 꼭 기억해 주세요.

[시작]
 1. @BeforeClass 실행 (최초 1회)
 
    2. @Before 실행
    3. @Test (첫 번째 테스트 메서드) 실행
    4. @After 실행
    
    5. @Before 실행
    6. @Test (두 번째 테스트 메서드) 실행
    7. @After 실행
    
 8. @AfterClass 실행 (마지막 1회)
[종료]

@BeforeClass

언제 : 해당 테스트 클래스가 실행될 때 가장 먼저, 딱 한 번만 실행됩니다.
특징 : 반드시 static 메서드로 작성해야 합니다. (예: 데이터베이스 연결 설정)

@Before
언제 : 각각의 @Test 메서드가 실행되기 직전에 매번 실행됩니다.
용도 : 테스트에 필요한 객체를 새로 만들거나, 초기 상태를 세팅할 때 씁니다.

@Test
언제 : 실제 테스트를 수행하는 본체입니다.
용도 : 여기서 코드의 결괏값을 검증(assertEquals 등 사용)합니다.

@After
언제 : 각각의 @Test 메서드가 끝난 직후에 매번 실행됩니다.
용도 : 사용한 자원을 정리하거나 초기화할 때 씁니다.

@AfterClass
언제 : 해당 클래스의 모든 테스트가 끝난 후, 딱 한 번만 실행됩니다.
특징 : 역시 static 메서드여야 합니다. (예: 데이터베이스 연결 해제)

4. 예시 코드

import org.junit.*;

public class MyCalculatorTest {

    @BeforeClass
    public static void setUpBeforeClass() {
        System.out.println("1. @BeforeClass: 시스템 세팅 (가장 먼저 한 번만)");
    }

    @Before
    public void setUp() {
        System.out.println("2. @Before: 계산기 전원 켜기 (각 테스트 전마다)");
    }

    @Test
    public void testAdd() {
        System.out.println("3. @Test: 더하기 테스트 실행!");
        // 여기서 실제 더하기 로직이 맞는지 검증합니다.
    }

    @Test
    public void testSubtract() {
        System.out.println("3. @Test: 빼기 테스트 실행!");
        // 여기서 실제 빼기 로직이 맞는지 검증합니다.
    }

    @After
    public void tearDown() {
        System.out.println("4. @After: 계산기 초기화 (각 테스트 후마다)");
    }

    @AfterClass
    public static void tearDownAfterClass() {
        System.out.println("5. @AfterClass: 시스템 종료 (모든 테스트가 끝나고 한 번만)");
    }
}

5. 테스트 순서 (Test Order)

꼭 알아두어야 할 핵심 규칙이 있습니다. 초보자분들이 가장 많이 헷갈려하시는 부분입니다.

"위에 작성한 코드 순서대로 @Test가 실행될까요?" 정답은 '아니오' 입니다.

 

JUnit4는 기본적으로 @Test 메서드들의 실행 순서를 보장하지 않습니다. 내부적인 해시코드 알고리즘 등에 의해 무작위처럼 보일 수 있게 실행됩니다.  이유는 각각의 단위 테스트는 서로에게 영향을 주지 않고 독립적이어야 한다는 프로그래밍 원칙 때문입니다. 테스트 A가 먼저 실행되어야만 테스트 B가 성공한다면, 그것은 잘못 짜인 테스트 코드입니다.

 

그럼 순서를 강제하고 싶다면? (권장하진 않지만) 클래스 위에 @FixMethodOrder(MethodSorters.NAME_ASCENDING)을 붙여주면, 메서드 이름의 알파벳 순서대로 실행시킬 수는 있습니다.

6. 기출문제

2015년 정보시스템감리사 28번 문제

 

앞서 설명해 드린 JUnit4의 실행 순서(라이프사이클) 개념을 정확히 묻고 있는 아주 좋은 문제입니다! 함께 차근차근 풀어볼까요? 문제에 주어진 SimpleTest 클래스에는 4개의 메서드가 있고, 각각 다음과 같은 어노테이션(명찰)이 붙어 있습니다. JUnit은 @Test가 붙은 메서드를 하나씩 실행하면서, 그 앞뒤로 @Before와 @After를 샌드위치처럼 끼워 넣습니다. 

setUp() : @Before가 붙어 있습니다.
각각의 @Test 메서드가 실행되기 직전에 매번 호출됩니다.


tearDown() : @After가 붙어 있습니다.
각각의 @Test 메서드가 끝난 직후에 매번 호출됩니다.


test1() : @Test가 붙어 있습니다. (첫 번째 테스트)

test2(): @Test가 붙어 있습니다. (두 번째 테스트)

 

첫 번째 테스트(test1) 실행 과정

  • 테스트 시작 전 무조건 @Before 실행 ➜ setUp()
  • 본격적인 테스트 실행 ➜ test1()
  • 테스트 종료 후 무조건 @After 실행 ➜ tearDown()
  • 현재까지의 순서 : setUp() ➜ test1() ➜ tearDown()

두 번째 테스트(test2) 실행 과정

  • 새로운 테스트 시작 전 다시 @Before 실행 ➜ setUp()
  • 본격적인 테스트 실행 ➜ test2()
  • 테스트 종료 후 다시 @After 실행 ➜ tearDown()
  • 추가된 순서 : setUp() ➜ test2() ➜ tearDown()

위 두 과정을 쭉 이어서 보면 전체 수행 순서는 다음과 같습니다.


③ setUp() ➜ test1() ➜ tearDown() ➜ setUp() ➜ test2() ➜ tearDown()

 

 

2023년 정보시스템감리사 46번 문제

 

앞서 배운 5가지 어노테이션이 모두 등장한, 이른바 '완전체' 문제군요! 처음에 설명해 드렸던 라이프사이클의 전체 흐름을 완벽하게 이해하고 있는지 묻는 아주 좋은 문제입니다. 위에서 풀었던 문제에 @BeforeClass와 @AfterClass만 추가되었으니, 배운 내용을 토대로 차근차근 조립해 보겠습니다.

 

코드에 붙은 어노테이션을 보고 언제 실행되는 녀석인지 파악해 봅니다.

  • a() : @BeforeClass 👉 클래스 시작 시 딱 1번 가장 먼저 실행.
  • b() : @AfterClass 👉 클래스 종료 시 딱 1번 가장 마지막에 실행.
  • c() : @Before 👉 각 @Test 실행 직전에 매번 실행.
  • d() : @After 👉 각 @Test 실행 직후에 매번 실행.
  • t1() : @Test 👉 첫 번째 테스트.
  • t2() : @Test 👉 두 번째 테스트.

이제 시간 순서대로 메서드들을 나열해 보겠습니다.

 

테스트 클래스 시작 : 가장 먼저 @BeforeClass가 한 번 실행됩니다. 

a()

 

첫 번째 테스트 (t1) 실행 : 테스트를 실행하기 전후로 샌드위치처럼 @Before와 @After가 감쌉니다.

@Before 실행 👉 c()

테스트 실행 👉 t1()

@After 실행 👉 d()


두 번째 테스트 (t2) 실행 : 새로운 테스트이므로 다시 @Before와 @After가 감쌉니다.

@Before 실행 👉 c()

테스트 실행 👉 t2()

@After 실행 👉 d()


테스트 클래스 종료 : 모든 테스트가 끝났으므로 마지막으로 @AfterClass가 한 번 실행됩니다.

b()

 

따라서 정답은 a() ➜ c() ➜ t1() ➜ d() ➜ c() ➜ t2() ➜ d() ➜ b()