Spring Boot JUnit5 이해하기 - 이론 및 구조

2025. 6. 10. 09:37spring/TDD

해당 글에서는 JUnit에 대해 이해하고 Spring Boot 환경에서 환경을 구성하는 방법에 대해 알아보고자 한다.

JUnit5

Java언어에서 독립된 단위 테스트(Unit Test)를 지원해주는 프레임워크

JUnit Jupiter의 주요 어노테이션

어노테이션 설명

@Test 테스트 메서드를 정의하는 데 사용되는 어노테이션입니다. 이 어노테이션이 지정된 메서드는 JUnit Jupiter에서 테스트로 실행됩니다.
@ParameterizedTest 매개변수화된 테스트를 정의하는 데 사용되는 어노테이션입니다. 이 어노테이션이 지정된 메서드는 여러 매개변수를 사용하여 반복적으로 실행됩니다.
@BeforeEach 각각의 테스트 메서드가 실행되기 전에 실행되는 코드를 정의하는 데 사용되는 어노테이션입니다.
@AfterEach 각각의 테스트 메서드가 실행된 후에 실행되는 코드를 정의하는 데 사용되는 어노테이션입니다.

JUnit5 어노테이션

어노테이션 설명

@Test 테스트 메소드임을 나타내는 어노테이션
@ParameterizedTest 매개변수화된 테스트를 위한 어노테이션
@RepeatedTest 반복적으로 실행되는 테스트를 위한 어노테이션
@TestFactory 동적으로 테스트를 생성하는 팩토리 메소드를 정의하는 어노테이션
@TestTemplate 테스트 템플릿을 정의하는 어노테이션
@TestClassOrder 테스트 클래스의 실행 순서를 지정하는 어노테이션
@TestMethodOrder 테스트 메소드의 실행 순서를 지정하는 어노테이션
@TestInstance 테스트 인스턴스의 생명주기를 지정하는 어노테이션
@DisplayName 테스트의 표시 이름을 설정하는 어노테이션
@DisplayNameGeneration 테스트의 표시 이름을 동적으로 생성하는 어노테이션
@BeforeEach 각 테스트 메소드 실행 전에 실행되는 메소드를 정의하는 어노테이션
@AfterEach 각 테스트 메소드 실행 후에 실행되는 메소드를 정의하는 어노테이션
@BeforeAll 모든 테스트 메소드 실행 전에 실행되는 메소드를 정의하는 어노테이션
@AfterAll 모든 테스트 메소드 실행 후에 실행되는 메소드를 정의하는 어노테이션
@Nested 중첩된 테스트 클래스를 정의하는 어노테이션
@Tag 테스트를 그룹화하기 위한 태그를 지정하는 어노테이션
@Disabled 비활성화된 테스트를 나타내는 어노테이션
@Timeout 테스트의 제한 시간을 설정하는 어노테이션
@ExtendWith 커스텀 확장을 적용하는 어노테이션
@RegisterExtension 커스텀 확장을 등록하는 어노테이션
@TempDir 테스트용 임시 디렉터리를 생성하는 어노테이션

JUnit5 Assertions 메소드

JUnit5 Assertions

  • 단위테스트에서는 다양한 유형의 검사를 수행하기 위한 일련의 Assertions메소드를 제공한다.
  • 이러한 Assertions는 테스트되는 코드의 예상동작을 확인하는데 도움이 된다.
  • import org.junit.jupiter.api.Assertions 패키지를 임포트 하여서 사용

메서드 구분 설명

assertEquals(expected, actual) JUnit 4~ 예상값과 실제값이 동일한지 확인합니다.
assertArrayEquals(expected, actual) JUnit 4~ 예상 배열과 실제 배열이 동일한지 확인합니다.
assertNull(object) JUnit 4~ 객체가 null인지 확인합니다.
assertNotNull(object) JUnit 4~ 객체가 null이 아닌지 확인합니다.
assertSame(expected, actual) JUnit 4~ 예상값과 실제값이 같은 객체인지 확인합니다.
assertNotSame(expected, actual) JUnit 4~ 예상값과 실제값이 다른 객체인지 확인합니다.
assertTrue(condition) JUnit 4~ 조건이 참인지 확인합니다.
assertFalse(condition) JUnit 4~ 조건이 거짓인지 확인합니다.
assertAll((Executable... executables) JUnit 5 모든 단언문(assertion)이 성공하는지 확인합니다.
assertIterableEquals(Iterable<?> expected, Iterable<?> actual) JUnit 5 예상 가능한 순서로 반복 가능한 항목이 동일한지 확인합니다.
assertLinesMatch((List<String> expectedLines, List<String> actualLines) JUnit 5 두 문자열 목록이 일치하는지 확인합니다.
assertNotEquals(Object unexpected, Object actual) JUnit 5 예상값과 실제값이 동일하지 않은지 확인합니다.
assertThrows(Class<T> expectedType, Executable executable) JUnit 5 지정된 예외가 발생하는지 확인합니다.
assertTimeout(Duration timeout, Executable executable) JUnit 5 주어진 시간 내에 작업이 완료되는지 확인합니다.
assertTimeoutPreemptively(Duration timeout, Executable executable) JUnit 5 주어진 시간 내에 작업이 완료되는지 확인하며, 작업이 중단될 수 있습니다


JUnit5 활용방안

JUnit 라이프 사이클 수행과정

  • 기본적으로 위에서 아래로 @Test 를 선언한 순서에 따라 수행이 된다.
  1. @BeforeAll JUnit 클래스가 수행되면 최초 한번 @BeforeAll 을 선언한 메소드가 실행
  2. @BeforeEach @Test를 찾았다면 테스트 실행 전 @BeforeEach 를 선언한 메소드가 실행
  3. @Test를 선언한 메소드가 실행
  4. @AfterEach @Test 실행을 마치면 @AfterEach 를 선언한 메소드가 실행
  5. @Test를 선언한 메서드가 있는지 찾으며 존재하면 (2. 과정)을 반복수행하며,
  6. 존재하지 않는 경우 (6. 과정)을 수행
  7. @AfterAll을 선언한 메서드를 수행
  8. 테스트 종료

AAA(Arrange - Act - Assert)  패턴

  • Arrange (준비), Act (실행), Asset(검증)의 약자로, 테스트코드를 구조화

단계 설명

Arrange (준비) 테스트에 필요한 객체나 데이터를 설정하는 단계로 테스트하려는 객체를 생성하고 모든 의존성을 준비합니다.
Act (실행) 준비된 객체나 데이터를 사용하여 실제로 테스트하려는 동작을 실행하는 단계입니다.
Assert (검증) 실행 결과가 예상한 대로인지 확인하는 단계. JUnit의 assert 메서드를 사용하여 결과를 검증합니다.
@SpringBootTest
class MyServiceTest {

    @Autowired
    private MyService myService;

    @Test
    void testCalculate() {
        // Arrange
        int input = 5;

        // Act
        int result = myService.calculate(input);

        // Assert
        assertEquals(10, result);
    }
}

Given-when-then 패턴

  • 테스트 케이스를 더 가독성 있고 유지보수하기 쉽게 구조화하는 방법을 의미
  • 이 패턴을 따르면 테스트 케이스가 더 구체적이고 이해하기 쉬워지며
  • 각각의 단계를 분리하여 각 테스트 부분이 어떤 역할을 담당하는지 명확

섹션 설명

Given : 설정 테스트의 초기 상태 또는 사전 조건을 설정합니다. 입력 데이터나 테스트가 실행될 문맥을 지정합니다.
When : 동작 테스트되는 동작 또는 이벤트를 설명합니다. 테스트되는 특정 메서드나 동작을 나타냅니다.
Then : 검증 "When" 섹션에서 설명한 동작으로 인해 기대되는 결과 또는 동작을 정의합니다.

Given 섹션에서는 '초기 상태'를 설정하고

When 섹션에서는 '특정 동작을 수행'하며,

Then 섹션에서는 '예상 결과를 검증'하는 어서션(assertion)을 사용하여 검증

import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import static org.junit.jupiter.api.Assertions.*;

class MyTest {

    @Test
    void testListSize() {
        // Given
        ArrayList<String> list = new ArrayList<>();

        // When
        list.add("Apple");
        list.add("Banana");
        list.add("Orange");

        // Then
        assertEquals(3, list.size());
        assertTrue(list.contains("Apple"));
        assertFalse(list.isEmpty());
    }
}

[ 더 알아보기 ]

💡 AAA 패턴이랑 Given-When-Then 패턴은 결국 똑같은거 아닌가?

  • 맞음. 모두 테스트 코드를 구조화하는 데 사용되는 패턴으로, 매우 유사. 각각의 단계는 다음과 같이 대응됨

1. Arrange (준비) = Given (주어진 상황)

  • 테스트에 필요한 객체나 데이터를 설정하는 단계

2. Act (실행) = When (실행할 동작)

  • 준비된 객체나 데이터를 사용하여 실제로 테스트하려는 동작을 실행

3. Assert (검증) = Then (예상 결과)

  • 실행 결과가 예상한 대로인지 확인하는 단계

JUnit 명명 규칙

💡 인기 있는 JUnit 명명 규칙

  • JUnit의 메서드명을 지정하는 명명 규칙에 대해 알아보자.
  • 이를 통해서 어떤 테스트를 수행하는지 명시적으로 이해할 수 있도록 돕는다.
  • 아래의 글을 참고하여서 작성

https://dzone.com/articles/7-popular-unit-test-naming

4.1. 명명 규칙 -1 : '메서드명_테스트대상상태_예상동작' 형식

💡 MethodName_StateUnderTest_ExpectedBehavior

  • '메소드명_테스트대상상태_예상동작' 형식으로 JUnit 메서드 명을 지정하는 방식

[ 사용 예시 ]

  • isAdult_False_AgeLessThan18: 만약 나이가 18보다 작으면 성인이 아님 (False)을 반환하는 경우
  • withdrawMoney_ThrowsException_IfAccountIsInvalid: 잘못된 계정으로 출금 시 예외가 발생하는 경우
  • admitStudent_FailToAdmit_IfMandatoryFieldsAreMissing: 필수 입력 필드가 누락되었을 때 학생을 입학시키지 못하는 경우

4.2. 명명 규칙 -2 : '메서드명_예상동작_테스트대상상태 '형식

💡 MethodName_ExpectedBehavior_StateUnderTest

  • “메서드명_예상동작_테스트대상상태” 형식으로 JUnit 메서드 명을 지정하는 방식입니다.

[사용 예시]

  • isAdult_False_AgeLessThan18: 만약 나이가 18보다 작으면 성인이 아님 (False)을 반환하는 경우
  • withdrawMoney_ThrowsException_IfAccountIsInvalid: 잘못된 계정으로 출금 시 예외가 발생하는 경우
  • admitStudent_FailToAdmit_IfMandatoryFieldsAreMissing: 필수 입력 필드가 누락되었을 때 학생을 입학시키지 못하는 경우

4.3. 명명 규칙 -3: “test” 접두어 형식

💡 test [Feature being tested]

  • “test” 접두어 형식으로 JUnit 메서드 명을 지정하는 방식입니다.

[사용 예시]

  • testIsNotAnAdultIfAgeLessThan18: 만약 나이가 18보다 작으면 성인이 아님을 테스트합니다.
  • testFailToWithdrawMoneyIfAccountIsInvalid: 잘못된 계정으로 출금 시 실패하는 것을 테스트합니다.
  • testStudentIsNotAdmittedIfMandatoryFieldsAreMissing: 필수 입력 필드가 누락되었을 때 학생이 입학되지 않는 것을 테스트합니다.

4.4. 명명 규칙-4 : 테스트할 기능 형식

💡 Feature to be tested

  • 일부는 메서드를 테스트 메서드로 식별하기 위해 이미 주석을 사용하고 있으므로, 단순히 테스트할 기능을 작성하는 것이 더 좋다고 제안합니다.

4.5. 명명 규칙 -5: Should_예상동작_테스트대상상태 형식

💡 Should_ExpectedBehavior_When_StateUnderTest

  • 'Should_예상동작_When_테스트대상상태'로 JUnit 메서드 명을 지정하는 방식입니다.

[사용예시]

  • Should_ThrowException_When_AgeLessThan18: 만약 나이가 18보다 작으면 예외를 던져야 합니다.
  • Should_FailToWithdrawMoney_ForInvalidAccount: 잘못된 계정일 경우 출금에 실패해야 합니다.
  • Should_FailToAdmit_IfMandatoryFieldsAreMissing: 필수 입력 필드가 누락되었을 때 입학에 실패해야 합니다.

4.6. 명명 규칙 -6: When_테스트대상상태_Expect_예상동작 형식

💡 When_StateUnderTest_Expect_ExpectedBehavior

  • 'When_테스트대상상태_Expect_예상동작'으로 JUnit 메서드 명을 지정하는 방식입니다.

[사용예시]

  • When_AgeLessThan18_Expect_isAdultAsFalse: 만약 나이가 18보다 작으면 성인이 아님 (False)을 예상합니다.
  • When_InvalidAccount_Expect_WithdrawMoneyToFail: 잘못된 계정으로 출금 시 실패할 것을 예상합니다.
  • When_MandatoryFieldsAreMissing_Expect_StudentAdmissionToFail: 필수 입력 필드가 누락되었을 때 학생 입학이 실패할 것을 예상합니다.

4.7. 명명 규칙 -7: Given-When-Then 형식

💡 Given_Preconditions_When_StateUnderTest_Then_ExpectedBehavior

  • Behavior-Driven Development (BDD)의 일부로 개발된 명명 규칙입니다. 테스트를 사전 조건 (Given), 테스트 대상 상태 (When), 예상 동작 (Then)으로 세 가지 부분으로 나누어 작성합니다.

[사용예시]

  • Given_UserIsAuthenticated_When_InvalidAccountNumberIsUsedToWithdrawMoney_Then_TransactionsWillFail: 사용자가 인증된 상태에서 잘못된 계정 번호를 사용하여 금액을 인출하면 거래가 실패할 것입니다.