
1. JUnit / JUnit5
JUnit은 자바에서 널리 쓰이는 단위 테스트 프레임워크로, 단위 테스트를 쉽게 작성하고 관리할 수 있게 해줍니다. JUnit 3.x에서 JUnit의 기초적인 구조가 완성되고, 이후 JUnit5까지 발전하였습니다.

JUnit 5는 JUnit의 최신 버전으로, JUnit Platform, JUnit Jupiter, JUnit Vintage로 구성된 모듈형 아키텍처로 설계되었습니다.
- JUnit Platform: JUnit 5의 핵심 플랫폼으로, 테스트를 실행하고 결과를 리포팅하는 역할을 합니다.
- JUnit Jupiter: JUnit 5에서 새로 추가된 테스트 프로그래밍 모델로, 새로운 어노테이션과 확장성을 제공합니다.
- JUnit Vintage: JUnit 3.x와 4.x 버전의 테스트를 JUnit 5 플랫폼에서 실행할 수 있도록 지원하는 모듈입니다.
2. JUnit5 테스트 생명주기
2.1. 인스턴스 관리: @TestInstance
JUnit 5는 각 테스트 메서드를 실행할 때 독립적인 테스트 인스턴스를 사용합니다. 즉, 테스트 클래스 내의 각각의 @Test 메서드가 실행될 때마다 새로운 인스턴스가 생성됩니다. 이를 "per-method" 인스턴스 생성이라고 합니다. 이 방식은 테스트 간의 의존성을 제거하고, 각각의 테스트가 독립적으로 수행될 수 있도록 보장합니다.
기본적으로는 위에서 설명한 대로 "per-method" 방식이지만, JUnit 5에서는 @TestInstance 어노테이션을 통해 인스턴스 관리 전략을 변경할 수 있습니다.
- @TestInstance(Lifecycle.PER_METHOD): 기본값으로, 각 테스트 메서드마다 새로운 인스턴스가 생성됩니다. 이를 통해 테스트 간 상태가 공유되지 않으므로 테스트의 독립성이 보장됩니다.
- @TestInstance(Lifecycle.PER_CLASS): 테스트 클래스당 하나의 인스턴스만 생성되며, 모든 테스트 메서드가 동일한 인스턴스를 공유하게 됩니다. 이를 통해 인스턴스 상태를 테스트 메서드 간에 공유할 수 있습니다. 예를 들어, 테스트 간 공유해야 할 데이터가 있거나 비용이 많이 드는 설정 작업을 피하고 싶을 때 유용할 수 있습니다.
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class ATest {
...
}
2.2. 테스트 인스턴스 생명주기 흐름
- 테스트 클래스가 로드됩니다.
- 각 테스트 메서드를 실행할 때마다 새로운 테스트 클래스 인스턴스가 생성됩니다.
- @BeforeAll, @BeforeEach 메서드가 먼저 실행되고, 그다음 @Test 메서드가 실행됩니다.
- 테스트 메서드가 종료되면, @AfterAll, @AfterEach 메서드가 실행됩니다.
- 테스트 클래스의 인스턴스는 테스트 메서드가 종료되면 더 이상 사용되지 않고 가비지 컬렉터에 의해 회수됩니다.
3. JUnit5의 어노테이션
3.1. @Test, @DisplayName
- @Test
- JUnit에서 테스트 메서드를 정의하는 가장 기본적인 어노테이션입니다.
- 이 어노테이션이 붙은 메서드는 테스트 대상이며, JUnit에 의해 자동으로 실행됩니다.
- @Test를 사용하여 테스트 케이스를 작성하고, 각 메서드는 독립적으로 실행됩니다.
@Test
void myTest() {
// 테스트 코드
}
- @DisplayName
- 테스트 메서드나 테스트 클래스의 이름을 보다 가독성 있게 지정하는 데 사용하는 어노테이션입니다.
- 기본적으로 테스트 메서드 이름이 그대로 출력되지만, @DisplayName을 사용하면 테스트 이름을 설명적으로 바꿔서 가독성을 높일 수 있습니다.
@Test
@DisplayName("Addition of two numbers")
void testAddition() {
// 테스트 코드
}
3.2. @BeforeAll, @AfterAll
- @BeforeAll
- 테스트 클래스 내에서 한 번만 실행되는 메서드를 지정하는 어노테이션입니다.
- 테스트 클래스 내에서 가장 먼저 실행되며, 주로 공유 리소스의 초기화나 설정에 사용됩니다.
- 메서드는 static으로 선언되어야 합니다(기본 생명주기 @TestInstance(Lifecycle.PER_METHOD)인 경우).
@BeforeAll
static void init() {
// 테스트 전체에 필요한 초기 설정
}
- @AfterAll
- 테스트 클래스 내에서 한 번만 실행되는 메서드를 지정하는 어노테이션입니다.
- 모든 테스트 메서드가 실행된 후에 마지막에 실행되며, 리소스 해제나 정리 작업을 수행하는 데 사용됩니다.
- @BeforeAll과 마찬가지로 static으로 선언되어야 합니다(기본 생명주기 @TestInstance(Lifecycle.PER_METHOD)인 경우).
@AfterAll
static void cleanup() {
// 테스트 완료 후 자원 해제 등 정리 작업
}
3.3. @BeforeEach, @AfterEach
- @BeforeEach
- 각 테스트 메서드가 실행되기 직전에 호출되는 메서드를 지정하는 어노테이션입니다.
- 모든 테스트 메서드마다 테스트 전 준비 작업을 수행합니다.
- 예를 들어, 객체 초기화나 설정 작업이 필요할 때 사용합니다.
@BeforeEach
void setUp() {
// 각 테스트 전에 실행되는 설정 작업
}
- @AfterEach
- 각 테스트 메서드가 실행된 직후에 호출되는 메서드를 지정하는 어노테이션입니다.
- 각 테스트 메서드가 끝난 후 정리 작업을 수행합니다.
@AfterEach
void tearDown() {
// 각 테스트 후에 실행되는 정리 작업
}
3.4. @repeatedTest
- 동일한 테스트 메서드를 여러 번 반복 실행할 수 있게 해주는 어노테이션입니다.
- 반복 횟수 정보 활용: RepeatedTest 메서드의 파라미터로 RepeatedTest.RepetitionInfo를 사용하면 반복 횟수 및 현재 반복 인덱스에 접근할 수 있습니다.
@RepeatedTest(3)
void repeatedTestWithRepetitionInfo(RepetitionInfo repetitionInfo) {
System.out.println("Repetition " + repetitionInfo.getCurrentRepetition() + " of " + repetitionInfo.getTotalRepetitions());
}
3.5. @Disabled
- 특정 테스트 메서드나 테스트 클래스의 실행을 비활성화하는 어노테이션입니다. 이를 통해 일시적으로 테스트를 건너뛰고, 추후 다시 활성화할 수 있습니다. 주로 개발 중 테스트가 실패하거나 현재 실행할 필요가 없는 경우에 사용됩니다.
@Disabled
@Test
void skippedTest() {
// 이 테스트는 실행되지 않습니다.
}
3.6. @nested
- 테스트 클래스를 중첩할 수 있게 해주는 어노테이션입니다.
- 테스트를 그룹화하고, 특정 조건이나 상황에 따라 테스트를 구성할 수 있습니다.
- 중첩 클래스는 상위 클래스의 필드와 메서드에 접근할 수 있어, 공통 설정을 재사용할 수 있습니다. 이를 통해 코드 중복을 줄일 수 있습니다.
- 보통 테스트 코드의 가독성 개선과 구조화 목적으로 사용됩니다.
class CalculatorTest {
@Nested
class WhenAdding {
@Test
void SumForTwoPositiveNumbers() {
// 두 양수 덧셈 테스트
}
@Test
void SumForPositiveAndNegativeNumber() {
// 양수와 음수 덧셈 테스트
}
}
@Nested
class WhenSubtracting {
@Test
void DifferenceForTwoPositiveNumbers() {
// 두 양수 뺄셈 테스트
}
@Test
void DifferenceForPositiveAndNegativeNumber() {
// 양수와 음수 뺄셈 테스트
}
}
}
3.7. @ParameterizedTest
- 파라미터화된 테스트를 작성할 수 있게 해주는 어노테이션입니다.
- 동일한 테스트 메서드를 다양한 입력 값과 기대 결과를 가지고 테스트할 수 있어, 코드의 다양한 경로를 검증할 수 있습니다.
- 입력 값을 제공하는 여러 가지 방법(예: @EmptySource, @ValueSource, @CsvSource, @MethodSource 등)을 지원합니다.
- @EmptySource: 비어 있는 값을 테스트할 때
@ParameterizedTest
@EmptySource
void isBlank_ShouldReturnTrueForEmptyStrings(String input) {
assertTrue(Strings.isBlank(input));
}
- @ValueSource: 단순한 값을 사용할 때
class MathUtilsTest {
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void testIsPositive(int number) {
assertTrue(number > 0, "Number should be positive");
}
}
- @CsvSource: 여러 개의 값과 기대 결과를 사용할 때
class CalculatorTest {
@ParameterizedTest
@CsvSource({
"1, 1, 2",
"2, 3, 5",
"4, 5, 9"
})
void testAddition(int a, int b, int expectedSum) {
assertEquals(expectedSum, a + b);
}
}
- @MethodSource: 외부 메서드를 통해 입력 값을 제공할 때
class StringUtilsTest {
@ParameterizedTest
@MethodSource("provideStringsForConcat")
void testConcat(String first, String second, String expected) {
assertEquals(expected, first + second);
}
static Stream<Arguments> provideStringsForConcat() {
return Stream.of(
Arguments.of("Hello", "World", "HelloWorld"),
Arguments.of("JUnit", "5", "JUnit5")
);
}
}
3.8. Assert 항목들
- assertEquals
- 두 개의 값이 동일한지를 검증하는 메서드입니다. 주로 테스트의 예상 결과와 실제 결과를 비교할 때 사용됩니다.
@Test
void testAddition() {
int expected = 5;
int actual = 2 + 3;
assertEquals(expected, actual, "2 + 3 should equal 5");
}
- assertThrows
- 예상하는 예외를 던지는지를 검증하는 메서드입니다. 예외가 발생하지 않으면 테스트는 실패합니다.
@Test
void testDivisionByZero() {
assertThrows(ArithmeticException.class, () -> {
int result = 1 / 0; // 0으로 나누기
});
}
- assertTimeout
- 주어진 시간 내에 완료되는지를 검증하는 메서드입니다. 코드 블록이 지정한 시간 내에 실행되지 않으면 테스트는 실패합니다.
@Test
void testTimeout() {
assertTimeout(Duration.ofMillis(100), () -> {
// 실행 시간 제한이 있는 코드
Thread.sleep(50); // 50ms 대기
});
}
- assertAll
- 여러 개의 assert를 그룹화하여 모두 검증할 수 있게 해주는 메서드입니다. 하나의 assert가 실패하더라도 나머지 assert는 영향을 받지 않고 계속 진행됩니다.
@Test
void testMultipleAssertions() {
int result = 5;
assertAll("result checks",
() -> assertEquals(5, result),
() -> assertTrue(result > 0),
() -> assertNotNull(result)
);
}
반응형
'컴퓨터 > JAVA' 카테고리의 다른 글
| JAVA - 일급 컬렉션(First-Class Collection) (0) | 2024.10.31 |
|---|---|
| JAVA - Enum 클래스 (0) | 2024.10.25 |
| 객체지향 - SOLID(객체 지향 설계) (0) | 2024.10.05 |
| JAVA - Generic(제너릭) (0) | 2024.09.30 |
| OOP - 객체지향프로그래밍 기초 - 인터페이스(Interface)의 응용(낮은 결합) (0) | 2023.04.25 |