Computer >> 컴퓨터 >  >> 프로그래밍 >> Java

JUnit 5 어노테이션 완벽 가이드: 예제 코드로 배우는 테스트 작성법

JUnit 5는 차세대 JUnit 프레임워크로, JVM 환경에서 개발자가 테스트를 작성할 수 있도록 최신 기반을 제공하는 것이 목표입니다. Java 8 이상을 지원하며 다양한 스타일의 테스트 작성을 가능하게 해줍니다.

JUnit 5 시작하기: 의존성 설정

JUnit 5는 Maven과 Gradle 모두에서 사용할 수 있습니다.

Maven을 사용한다면 pom.xml 파일에 아래 의존성을 추가하세요:

<dependencies>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.3.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-params</artifactId>
        <version>5.3.1</version>
        <scope>test</scope>
    </dependency>
</dependencies>

JUnit 5에서 눈에 띄는 가장 큰 변화 중 하나는 테스트 클래스와 메서드가 더 이상 public일 필요가 없다는 점입니다. 패키지 수준으로 선언해도 정상적으로 동작합니다.

이제 가장 자주 사용되는 JUnit 5 어노테이션들을 하나씩 살펴보겠습니다.

@Test

@Test 어노테이션은 해당 메서드가 테스트 메서드임을 나타냅니다. 이 어노테이션은 별도의 속성을 받지 않습니다.

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

class JUnit5Test {
        
    @Test
    void helloJUnit5() {
        assertEquals(10, 5+5);
    }
}

@ParameterizedTest

@ParameterizedTest를 사용하면 서로 다른 인자를 넘겨 하나의 테스트를 여러 번 실행할 수 있습니다. 일반적인 @Test 메서드처럼 선언하지만, 대신 @ParameterizedTest 어노테이션을 사용합니다.

또한 각 실행에 전달할 인자를 제공하는 소스(source)를 최소 한 개 이상 선언하고, 테스트 메서드에서 해당 인자들을 소비(consume)해야 합니다.

아래 예제는 @ValueSource 어노테이션을 사용해 문자열 배열을 인자 소스로 지정하는 파라미터화된 테스트를 보여줍니다.

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import static org.junit.jupiter.api.Assertions.assertTrue;

class JUnit5Test {

    @ParameterizedTest
    @ValueSource(strings = { "cali", "bali", "dani" })
    void endsWithI(String str) {
        assertTrue(str.endsWith("i"));
    }
}

@RepeatedTest

JUnit 5에서는 메서드에 @RepeatedTest 어노테이션을 붙이고 원하는 반복 횟수를 지정하는 것만으로 테스트를 여러 번 반복 실행할 수 있습니다.

반복 테스트의 각 실행은 일반적인 @Test 메서드의 실행과 동일하게 동작합니다.

특히 Selenium을 활용한 UI 테스트에서 유용하게 활용됩니다.

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.RepetitionInfo;
import org.junit.jupiter.api.TestInfo;

import static org.junit.jupiter.api.Assertions.assertEquals;

class JUnit5Test {
    
    @RepeatedTest(value = 5, name = "{displayName} {currentRepetition}/{totalRepetitions}")
    @DisplayName("RepeatingTest")
    void customDisplayName(RepetitionInfo repInfo, TestInfo testInfo) {
        int i = 3;
        System.out.println(testInfo.getDisplayName() + 
            "-->" + repInfo.getCurrentRepetition()
        );
        
        assertEquals(repInfo.getCurrentRepetition(), i);
    }
}

테스트 결과에서 확인할 수 있듯이, i==3일 때만 테스트가 통과하고 그 외에는 실패합니다.

@DisplayName

테스트 클래스와 테스트 메서드에는 커스텀 표시 이름을 지정할 수 있으며, 이 이름은 테스트 러너와 테스트 리포트에 그대로 노출됩니다.

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;

@DisplayName("DisplayName Demo")
class JUnit5Test {
    @Test
    @DisplayName("Custom test name")
    void testWithDisplayName() {
    }

    @Test
    @DisplayName("Print test name")
    void printDisplayName(TestInfo testInfo) {
        System.out.println(testInfo.getDisplayName());
    }
}

@BeforeEach

@BeforeEach 어노테이션이 붙은 메서드는 각 테스트 메서드가 실행되기 전에 매번 실행됩니다. JUnit 4의 @Before와 동일한 역할을 합니다.

import org.junit.jupiter.api.*;

class JUnit5Test {
    @BeforeEach
    void init(TestInfo testInfo) {
        String callingTest = testInfo.getTestMethod().get().getName();
        System.out.println(callingTest);
    }

    @Test
    void firstTest() {
        System.out.println(1);
    }

    @Test
    void secondTest() {
        System.out.println(2);
    }
}

실행 결과:

firstTest
1
secondTest
2

@AfterEach

@AfterEach 어노테이션이 붙은 메서드는 각 테스트 메서드가 실행된 후에 매번 실행됩니다. JUnit 4의 @After에 해당합니다. 예를 들어 각 테스트가 끝난 후 특정 속성을 초기화해야 한다면, 해당 작업을 수행하는 메서드에 @AfterEach를 붙이면 됩니다.

import org.junit.jupiter.api.*;

class JUnit5Test {

    @Test
    void firstTest() {
        System.out.println(1);
    }
    @Test
    void secondTest() {
        System.out.println(2);
    }

    @AfterEach
    void after(TestInfo testInfo) {
        String callingTest = testInfo.getTestMethod().get().getName();
        System.out.println(callingTest);
    }
}

실행 결과:

1
firstTest
2
secondTest

@BeforeAll

@BeforeAll은 모든 테스트가 시작되기 전에 단 한 번 실행됩니다. JUnit 4의 @BeforeClass와 유사하며, 주로 테스트에 필요한 각종 리소스나 설정을 초기화할 때 사용합니다.

import org.junit.jupiter.api.*;

class JUnit5Test {

    @BeforeAll
    static void init() {
        System.out.println("Only run once before all tests");
    }

    @Test
    void firstTest() {
        System.out.println(1);
    }
    @Test
    void secondTest() {
        System.out.println(2);
    }
}

실행 결과:

Only run once before all tests
1
2

@AfterAll

@AfterAll 어노테이션이 붙은 메서드는 모든 테스트가 종료된 후 단 한 번 실행됩니다. JUnit 4의 @AfterClass에 해당하며, 모든 테스트가 끝난 시점에 프로세스를 정리하거나 종료할 때 사용합니다.

import org.junit.jupiter.api.*;

class JUnit5Test {

    @Test
    void firstTest() {
        System.out.println(1);
    }
    @Test
    void secondTest() {
        System.out.println(2);
    }

    @AfterAll
    static void after() {
        System.out.println("Only run once after all tests");
    }
}

실행 결과:

1
2
Only run once after all tests

@Tag

@Tag 어노테이션을 사용하면 클래스 또는 메서드 수준에서 태그를 선언해 테스트를 필터링할 수 있습니다.

선택한 테스트들만 묶어 별도의 테스트 팩(test pack)을 구성하고 싶을 때 특히 유용합니다.

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

@Tag("smoke")
class JUnit5Test {

    @Test
    @Tag("login")
    void validLoginTest() {
    }

    @Test
    @Tag("search")
    void searchTest() {
    }
}

@Disabled

@Disabled 어노테이션은 클래스 또는 메서드 수준에서 테스트를 비활성화하거나 건너뛸 때 사용합니다. JUnit 4의 @Ignore에 해당합니다.

클래스 수준에서 선언하면 해당 클래스의 모든 @Test 메서드가 건너뛰어지고, 메서드 수준에서 사용하면 해당 메서드만 비활성화됩니다.

테스트 클래스를 비활성화하는 예제:

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

@Disabled
class DisabledClassDemo {

    @Test
    void testWillBeSkipped() {
    }
}

테스트 메서드를 비활성화하는 예제:

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

class DisabledTestsDemo {

    @Disabled
    @Test
    void testWillBeSkipped() {
    }

    @Test
    void testWillBeExecuted() {
    }
}