TestCode

JUnit4와 JUnit5의 차이

누구세연 2025. 1. 27. 12:34

Java 애플리케이션의 단위 테스트에서 널리 사용되는 JUnit은 개발자에게 필수적인 도구입니다. 🛠️

저는 평소 JUnit5를 사용해 왔지만, 최근에 JUnit4를 사용해 볼 기회가 생겼습니다.

처음에는 익숙하지 않은 버전이라 조금 낯설었지만, 이를 통해 두 버전의 차이를 직접 체감하며 JUnit의 발전을 이해할 수 있었습니다.

이 글에서는 JUnit4와 JUnit5의 차이점, 장단점, 그리고 제가 사용하면서 느낀 점을 정리해 보았습니다.👩🏻‍💻

 

JUnit4의 특징

JUnit4는 간단하고 직관적인 테스트 프레임워크로, 다음과 같은 어노테이션을 제공합니다.

 

  • @Test: 테스트 메서드 표시.
  • @Before / @After: 테스트 전/후에 실행되는 메서드 지정.
  • @Ignore: 특정 테스트를 비활성화.
  • @RunWith: 테스트 실행을 커스터마이징 하기 위한 기능.

장점

  • 직관적인 사용법
  • 간단한 프로젝트에서는 빠르게 적용 가능

단점

  • 확장성 부족: 확장을 위해 복잡한 커스텀 Runner를 만들어야 함
  • 가독성 부족: 테스트 이름을 명확히 지정할 수 있는 @DisplayName 기능이 없음
  • 조건부 테스트 지원 부족: 직접 조건문을 작성하거나 Assume API를 사용해야 함
@RunWith(MockitoJUnitRunner.class)
public class ExampleTest {

    @Before
    public void setup() {
        // 테스트 준비 코드
    }

    @Test
    public void testExample() {
        assertEquals(2, 1 + 1);
    }
}

 

JUnit5의 특징

JUnit5는 JUnit4의 한계를 개선하고, 더 많은 기능과 확장성을 제공합니다.

모듈화

JUnit5는 3개의 모듈로 구성되어 있습니다:

  • Jupiter: JUnit5의 핵심 API.
  • Vintage: JUnit4와 호환성 유지.
  • Platform: 테스트 실행 환경.

새로운 어노테이션

  • @BeforeEach / @AfterEach: JUnit4의 @Before / @After 대체.
  • @DisplayName: 테스트 이름을 명시적으로 지정.
  • @Nested: 중첩된 테스트 클래스 지원.

확장성

  • @ExtendWith를 통해 다양한 확장 기능을 손쉽게 추가.

Java 8 이상

  • 람다 표현식과 스트림 API를 활용하여 간결한 테스트 작성 가능.
@ExtendWith(MockitoExtension.class)
class ExampleTest {

    @BeforeEach
    void setup() {
        // 테스트 준비 코드
    }

    @Test
    @DisplayName("간단한 덧셈 테스트")
    void testExample() {
        assertEquals(2, 1 + 1);
    }
}

 

 

 

JUnit4와 JUnit5의 주요 차이점 ⚖️

기능 JUnit4 JUnit5
초기화 어노테이션 @Before / @After @BeforeEach / @AfterEach
비활성화 어노테이션 @Ignore @Disabled
테스트 이름 지정 N/A @DisplayName
확장성 @RunWith @ExtendWith
조건부 테스트 지원 Assume API @EnabledOnOs, @EnabledIf 등
병렬 실행 지원 미지원 지원
테스트 실행 환경 단일 Runner 다중 환경 기능

 

조건부 테스트

JUnit5: @EnabledOnOs

JUnit5는 다양한 조건부 어노테이션을 제공하여 간단히 조건부 테스트를 작성할 수 있습니다.

@Test
@EnabledOnOs(OS.WINDOWS)
void testOnlyOnWindows() {
    System.out.println("This test runs only on Windows");
}

@Test
@EnabledOnOs(OS.LINUX)
void testOnlyOnLinux() {
    System.out.println("This test runs only on Linux");
}

 

JUnit4: Assume API

JUnit4에서는 조건부 테스트를 작성하기 위해 Assume API를 사용해야 합니다.

import org.junit.Test;
import org.junit.Assume;

public class ConditionalTestExample {

    @Test
    public void testOnlyOnWindows() {
        String osName = System.getProperty("os.name").toLowerCase();
        Assume.assumeTrue(osName.contains("win"));
        System.out.println("This test runs only on Windows");
    }

    @Test
    public void testOnlyOnLinux() {
        String osName = System.getProperty("os.name").toLowerCase();
        Assume.assumeTrue(osName.contains("linux"));
        System.out.println("This test runs only on Linux");
    }
}

JUnit5의 조건부 테스트는 @EnabledOnOs, @EnabledIf 같은 어노테이션을 통해 특정 조건에서 테스트를 실행할 수 있습니다.
이는 복잡한 조건문을 직접 작성하지 않아도 되므로, 테스트 코드의 가독성을 높이고 유지보수를 더 쉽게 만들어줍니다.

 

JUnit4 사용 경험에서 느낀 아쉬움과 배움

확장성의 제한

왜 JUnit4는 확장성이 제한되었다고 할까⁉️

JUnit4에서는 확장을 적용하려면 커스텀 Runner를 만들어야 합니다. 하나의 테스트 클래스는 하나의 Runner만 사용할 수 있기 때문에 특정 요구사항을 처리하려면 Runner를 직접 작성해야 합니다.

예를 들어, 아래는 JUnit4에서 특정 설정을 추가하려고 만든 커스텀 Runner의 코드입니다.

public class CustomRunner extends BlockJUnit4ClassRunner {

    public CustomRunner(Class<?> klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected Statement withBeforeClasses(Statement statement) {
        // 추가 설정
        System.out.println("Before All Tests");
        return super.withBeforeClasses(statement);
    }
}

테스트 클래스에서 커스텀 Runner 사용

@RunWith(CustomRunner.class) // Runner를 직접 지정
public class CustomRunnerTest {

    @Test
    public void testCustomLogic() {
        assertEquals(2, 1 + 1);
    }
}

 

JUnit5는 왜 더 편리할까⁉️

JUnit5에서는 확장을 적용하려면 @ExtendWith를 사용합니다. Runner를 직접 작성하지 않고, 확장 클래스만 작성하면 됩니다.

예를 들어, JUnit5에서는 아래와 같이 확장 클래스만 정의하여 테스트 클래스에서 필요한 기능을 쉽게 추가할 수 있습니다.

public class MyCustomExtension implements BeforeTestExecutionCallback {
    @Override
    public void beforeTestExecution(ExtensionContext context) {
        System.out.println("Before Test Execution: " + context.getDisplayName());
    }
}

테스트 클래스에서 확장 적용

@ExtendWith(MyCustomExtension.class) // 확장 클래스 적용
class CustomExtensionTest {

    @Test
    void testCustomLogic() {
        assertEquals(2, 1 + 1);
    }
}

JUnit5는 한 테스트 클래스에 여러 확장(@ExtendWith)을 적용할 수 있습니다.
반면, JUnit4는 한 번에 하나의 Runner만 사용할 수 있으므로 확장성이 제한됩니다.

 

가독성 부족

JUnit4에서는 테스트 메서드의 의미를 명확히 드러내기 어렵다는 점이 있었습니다. 예를 들어, 테스트 메서드 이름만으로 테스트의 목적을 이해하기 어려울 때가 있었습니다.

JUnit4의 테스트 코드

@Test
public void additionTest() {
    assertEquals(2, 1 + 1);
}

 

JUnit5에서 @DisplayName을 활용한 테스트 코드

@DisplayName("간단한 덧셈 테스트")
@Test
void testAddition() {
    assertEquals(2, 1 + 1);
}

JUnit5의 @DisplayName은 테스트 이름을 명시적으로 작성할 수 있어,
테스트 의도를 더 명확히 표현하고, 결과를 한눈에 파악하기 쉽게 만들어줍니다.

 

Mock 객체 활용의 복잡성

JUnit4에서 반복된다는 의미

JUnit4에서는 Mock 객체를 활용하려면 항상 @RunWith(MockitoJUnitRunner.class)를 사용해야 했습니다.
이것은 테스트 클래스마다 지정해주어야 하며, 커스텀 Runner를 사용할 경우 중복 설정이 발생합니다.

예를 들어, 아래 테스트 클래스는 Mock 객체를 사용하려고 MockitoJUnitRunner를 반드시 지정해야 합니다.

@RunWith(MockitoJUnitRunner.class) // 이 부분은 모든 Mock 객체 사용 테스트에서 필요
public class MockExampleTest {

    @Mock
    private SomeService someService;

    @Test
    public void testService() {
        when(someService.getData()).thenReturn("Mock Data"); // Mock 데이터 설정
        assertEquals("Mock Data", someService.getData());
    }
}

만약 다른 Runner(예: SpringJUnit4 ClassRunner)를 사용해야 하는 테스트 환경에서는 Mock 객체 사용을 위한 추가 설정이 필요합니다.
결과적으로 Mock 객체 설정이 반복되거나 추가 코드로 인해 코드가 복잡해질 수 있습니다.

 

JUnit5는 어떻게 해결했을까⁉️

JUnit5에서는 @ExtendWith(MockitoExtension.class)를 사용합니다.
Runner 대신 확장 기능(Extension)으로 Mocking을 지원하므로, Mock 객체 설정과 테스트 환경을 더 유연하게 결합할 수 있습니다.

예를 들어, 아래처럼 Mock 객체 설정과 다른 확장 기능(예: Spring의 확장)도 함께 적용할 수 있습니다.

@ExtendWith(MockitoExtension.class) // Mock 객체 설정
@ExtendWith(SpringExtension.class)  // Spring 확장
class MockExampleTest {

    @Mock
    private SomeService someService;

    @Test
    void testService() {
        when(someService.getData()).thenReturn("Mock Data");
        assertEquals("Mock Data", someService.getData());
    }
}

 

 

 

💡 JUnit4는 간단한 테스트 환경에서는 충분히 효과적이었지만, 확장성과 가독성 측면에서 JUnit5가 더 나은 경험을 제공한다고 느꼈습니다. 특히, JUnit5의 간단한 설정과 명확한 테스트 표현 방식은 테스트 작성 및 유지보수의 효율성을 크게 높여줄 수 있을 것이라 생각합니다.

 

 

'TestCode' 카테고리의 다른 글

인수 테스트 격리하는 방법  (0) 2024.07.06
FixtureMonkey 알아보기  (0) 2024.03.26
단위 테스트 vs 통합 테스트 vs 인수테스트  (1) 2024.02.10